From 81277006217731f104e4248c4d32c0391a1a451f Mon Sep 17 00:00:00 2001 From: cpmachado <40689339+cpmachado@users.noreply.github.com> Date: Tue, 29 Oct 2024 11:18:09 +0000 Subject: [PATCH] Refactor to have libegc (#7) * Replicate computation of egyptian fractions * rename egc.c -> main.c * Rename lib * Integrate lib * Adapt linter checks * Check cppcheck version * Check help for cppcheck version 2.7 * Try to snap install cppcheck =2.14 * Remove -y from snap install * Remove redundant cppcheck commands in workflow --- .github/workflows/ci.yml | 2 +- CPPLINT.cfg | 1 + Makefile | 29 +++++++++-- include/egc.h | 13 +++++ src/egc/egc.c | 48 +++++++++++++++++ src/{egc.c => main.c} | 108 +++++++++++---------------------------- 6 files changed, 118 insertions(+), 83 deletions(-) create mode 100644 include/egc.h create mode 100644 src/egc/egc.c rename src/{egc.c => main.c} (61%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 81b3807..041b7bd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: with: python-version: '3.9' - run: pip install cpplint===1.6.1 - - run: sudo apt install cppcheck -y + - run: sudo snap install cppcheck - run: make lint dist: runs-on: ubuntu-latest diff --git a/CPPLINT.cfg b/CPPLINT.cfg index 36d9cb2..aad5be5 100644 --- a/CPPLINT.cfg +++ b/CPPLINT.cfg @@ -1 +1,2 @@ set noparent +filter=-build/include_subdir,-build/header_guard diff --git a/Makefile b/Makefile index 560a5a3..6c0aba6 100644 --- a/Makefile +++ b/Makefile @@ -23,6 +23,12 @@ BIN_DIR = $(BUILD_DIR)/bin DIST_DIR = $(BUILD_DIR)/dist DIST_BASE_DIR = $(DIST_DIR)/egc-$(VERSION) +LIBEGC_SRC_DIR = src/egc +LIBEGC_OBJ_DIR = $(BUILD_DIR)/obj/egc +LIBEGC_SRC = $(wildcard $(LIBEGC_SRC_DIR)/*.c) +LIBEGC_OBJ = $(patsubst $(LIBEGC_SRC_DIR)/%.c,$(LIBEGC_OBJ_DIR)/%.o, $(LIBEGC_SRC)) +LIBEGC_BIN = $(BUILD_DIR)/libegc.a + SRC = $(wildcard $(SRC_DIR)/*.c) OBJ = $(patsubst $(SRC_DIR)/%.c,$(OBJ_DIR)/%.o, $(SRC)) BIN = $(BIN_DIR)/egc @@ -36,8 +42,8 @@ clean: @rm -rf $(BUILD_DIR) lint: - @cppcheck $(SRC_DIR) - @cpplint --recursive $(SRC_DIR) + @cppcheck --check-level=exhaustive $(SRC_DIR) include + @cpplint --recursive $(SRC_DIR) include options: @echo "egc compilation flags" @@ -46,6 +52,9 @@ options: @echo "CFLAGS = $(CFLAGS)" @echo "CPPFLAGS = $(CPPFLAGS)" @echo "LDFLAGS = $(LDFLAGS)" + @echo "LIBEGC_SRC = $(LIBEGC_SRC)" + @echo "LIBEGC_OBJ = $(LIBEGC_OBJ)" + @echo "LIBEGC_BIN = $(LIBEGC_BIN)" @echo "SRC = $(SRC)" @echo "OBJ = $(OBJ)" @echo "BIN = $(BIN)" @@ -67,16 +76,26 @@ uninstall: @echo removing executable file from $(PREFIX)/bin @rm -f $(PREFIX)/bin/egc +$(BUILD_DIR): + mkdir -p $(BUILD_DIR) + $(OBJ_DIR): mkdir -p $(OBJ_DIR) +$(LIBEGC_OBJ_DIR): + mkdir -p $(LIBEGC_OBJ_DIR) + $(BIN_DIR): mkdir -p $(BIN_DIR) -$(BIN): $(OBJ) | $(BIN_DIR) +$(LIBEGC_BIN): $(LIBEGC_OBJ) | $(BUILD_DIR) + ar rcs $@ $^ + +$(BIN): $(OBJ) $(LIBEGC_BIN) | $(BIN_DIR) $(CC) -o $@ $^ $(LDFLAGS) + +$(LIBEGC_OBJ_DIR)/%.o: $(LIBEGC_SRC_DIR)/%.c | $(LIBEGC_OBJ_DIR) + $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< $(OBJ_DIR)/%.o: $(SRC_DIR)/%.c | $(OBJ_DIR) $(CC) $(CFLAGS) $(CPPFLAGS) -c -o $@ $< - -$(OBJ_DIR)/egc.o: $(SRC_DIR)/egc.c diff --git a/include/egc.h b/include/egc.h new file mode 100644 index 0000000..cd2fa06 --- /dev/null +++ b/include/egc.h @@ -0,0 +1,13 @@ +/* Copyright 2024 cpmachado */ + +#define MAX_FRACTIONS_EGYPTIAN 100 + +typedef struct Fraction { + int64_t num, den; +} Fraction; + +typedef struct EgyptianFraction { + int64_t dens[MAX_FRACTIONS_EGYPTIAN], n; +} EgyptianFraction; + +int64_t computeEgyptianFraction(Fraction *fraction, EgyptianFraction *egyptian); diff --git a/src/egc/egc.c b/src/egc/egc.c new file mode 100644 index 0000000..bbcd832 --- /dev/null +++ b/src/egc/egc.c @@ -0,0 +1,48 @@ +/* Copyright 2024 cpmachado */ + +#include +#include + +#include "egc.h" + +int64_t gcd(int64_t a, int64_t b) { + int64_t c; + while (b > 0) { + if (a > b) { + c = a; + a = b; + b = c % a; + } else { + b = b % a; + } + } + return a; +} + +int64_t computeEgyptianFraction(Fraction *fraction, + EgyptianFraction *egyptian) { + int64_t num = fraction->num, den = fraction->den; + int64_t i, gcd_val, n; + + for (i = 0; num > 1; i++) { + gcd_val = gcd(num, den); + num /= gcd_val; + den /= gcd_val; + n = den / num + (den % num > 0); + egyptian->dens[i] = n; + if (INT64_MAX / n < num) { + fprintf(stderr, "Overflow detected: (num, n) = (%ld, %ld)\n", num, n); + return -1; + } + num = num * n - den; + if (INT64_MAX / n < den) { + fprintf(stderr, "Overflow detected: (den, n) = (%ld, %ld)\n", den, n); + return -1; + } + den = den * n; + } + egyptian->dens[i] = den; + i++; + egyptian->n = i; + return i; +} diff --git a/src/egc.c b/src/main.c similarity index 61% rename from src/egc.c rename to src/main.c index 9c44ca7..35147c2 100644 --- a/src/egc.c +++ b/src/main.c @@ -8,6 +8,8 @@ #include #include +#include "egc.h" + #ifndef VERSION #define VERSION "unknown" #endif @@ -23,18 +25,16 @@ void version(void); int64_t readPositiveInt64(void); -void csvOutput(int64_t *s, int64_t n); - -void simpleOutput(int64_t *s, int64_t n); +void csvOutput(EgyptianFraction *s); -void straightOutput(int64_t *s, int64_t n); +void simpleOutput(EgyptianFraction *s); -int32_t computeUnitaryFractions(int64_t num, int64_t den, int64_t *s); +void straightOutput(EgyptianFraction *s); int32_t main(int32_t argc, char **argv) { - int64_t s[BUFSIZ]; int64_t n = 0; - int64_t num = -1, den = -1; + Fraction fraction = {.num = -1, .den = -1}; + EgyptianFraction egyptian; int32_t csv = 0, straight = 0; int opt; @@ -63,7 +63,7 @@ int32_t main(int32_t argc, char **argv) { straight = 1; break; case 'n': - num = strtoll(optarg, NULL, 10); + fraction.num = strtoll(optarg, NULL, 10); if (errno) { fprintf(stderr, "%s", strerror(errno)); usage(); @@ -71,7 +71,7 @@ int32_t main(int32_t argc, char **argv) { } break; case 'd': - den = strtoll(optarg, NULL, 10); + fraction.den = strtoll(optarg, NULL, 10); if (errno) { fprintf(stderr, "%s", strerror(errno)); usage(); @@ -84,27 +84,30 @@ int32_t main(int32_t argc, char **argv) { exit(EXIT_FAILURE); } } - if (num <= 0) { + if (fraction.num <= 0) { printf("numerator: "); - num = readPositiveInt64(); + fraction.num = readPositiveInt64(); } - if (den <= 0) { + if (fraction.den <= 0) { printf("denominator: "); - den = readPositiveInt64(); + fraction.den = readPositiveInt64(); } - if (num > den || num <= 0) { + if (fraction.num > fraction.den || fraction.num <= 0) { fprintf(stderr, "r not in ]0, 1[\n"); exit(EXIT_FAILURE); } - n = computeUnitaryFractions(num, den, s); + if ((n = computeEgyptianFraction(&fraction, &egyptian)) <= 0) { + fprintf(stderr, "Failed computeEgyptianFration with code %ld\n", n); + exit(EXIT_FAILURE); + } if (csv) { - csvOutput(s, n); + csvOutput(&egyptian); } else if (straight) { - straightOutput(s, n); + straightOutput(&egyptian); } else { - simpleOutput(s, n); + simpleOutput(&egyptian); } return 0; } @@ -129,55 +132,6 @@ void version(void) { ": cpmachado\n"); } -int64_t gcdInt64(int64_t a, int64_t b) { - int64_t c; - while (b > 0) { - if (a > b) { - c = a; - a = b; - b = c % a; - } else { - b = b % a; - } - } - return a; -} - -int32_t computeUnitaryFractions(int64_t num, int64_t den, int64_t *s) { - int64_t n, i; - - s[0] = 0; - - for (i = 1; num > 1; i++) { - /* normalise denominator and numerator */ - n = gcdInt64(num, den); - num = num / n; - den = den / n; - - /* The real n */ - n = den / num + (den % num > 0); - s[i] = n; - if (INT64_MAX / n < num) { - fprintf(stderr, "Overflow detected\n"); - printf("num = %ld, n = %ld\n", num, n); - exit(EXIT_FAILURE); - } - num = num * n - den; - if (INT64_MAX / n < den) { - fprintf(stderr, "Overflow detected\n"); - printf("den = %ld, n = %ld\n", den, n); - exit(EXIT_FAILURE); - } - den = den * n; - } - - if (num > 0) { - s[i] = den; - i++; - } - return i; -} - int64_t readPositiveInt64() { char buf[BUFSIZ]; int64_t n; @@ -195,30 +149,30 @@ int64_t readPositiveInt64() { return n; } -void csvOutput(int64_t *s, int64_t n) { +void csvOutput(EgyptianFraction *s) { int64_t i; printf("i,n_i\n"); - for (i = 1; i < n; i++) { - printf("%ld,%ld\n", i, s[i]); + for (i = 0; i < s->n; i++) { + printf("%ld,%ld\n", i, s->dens[i]); } } -void simpleOutput(int64_t *s, int64_t n) { +void simpleOutput(EgyptianFraction *s) { int64_t i; - for (i = 1; i < n; i++) { - printf("%ld\n", s[i]); + for (i = 0; i < s->n; i++) { + printf("%ld\n", s->dens[i]); } } -void straightOutput(int64_t *s, int64_t n) { +void straightOutput(EgyptianFraction *s) { int64_t i; - for (i = 1; i < n; i++) { - printf("(%ld, %ld)", i, s[i]); - if (i < n - 1) { + for (i = 0; i < s->n; i++) { + printf("(%ld, %ld)", i, s->dens[i]); + if (i < s->n - 1) { putchar(','); } }