From 5a35db2396a81626584ff4fb257a163b925b5b6e Mon Sep 17 00:00:00 2001 From: Bruno Raoult Date: Wed, 25 Aug 2021 20:35:55 +0200 Subject: [PATCH] C: pythagorean-triplet and saddle-point --- c/pythagorean-triplet/GNUmakefile | 51 +++++++ c/pythagorean-triplet/README.md | 62 ++++++++ c/pythagorean-triplet/makefile | 37 +++++ .../src/pythagorean_triplet.c | 89 +++++++++++ .../src/pythagorean_triplet.h | 34 +++++ c/saddle-points/GNUmakefile | 51 +++++++ c/saddle-points/README.md | 67 +++++++++ c/saddle-points/makefile | 37 +++++ c/saddle-points/src/saddle_points.c | 142 ++++++++++++++++++ c/saddle-points/src/saddle_points.h | 36 +++++ 10 files changed, 606 insertions(+) create mode 100644 c/pythagorean-triplet/GNUmakefile create mode 100644 c/pythagorean-triplet/README.md create mode 100644 c/pythagorean-triplet/makefile create mode 100644 c/pythagorean-triplet/src/pythagorean_triplet.c create mode 100644 c/pythagorean-triplet/src/pythagorean_triplet.h create mode 100644 c/saddle-points/GNUmakefile create mode 100644 c/saddle-points/README.md create mode 100644 c/saddle-points/makefile create mode 100644 c/saddle-points/src/saddle_points.c create mode 100644 c/saddle-points/src/saddle_points.h diff --git a/c/pythagorean-triplet/GNUmakefile b/c/pythagorean-triplet/GNUmakefile new file mode 100644 index 0000000..adf038d --- /dev/null +++ b/c/pythagorean-triplet/GNUmakefile @@ -0,0 +1,51 @@ +# The original 'makefile' has a flaw: +# 1) it overrides CFLAGS +# 2) it does not pass extra "FLAGS" to $(CC) that could come from environment +# +# It means : +# - we need to edit 'makefile' for different builds (DEBUG, etc...), which is +# not practical at all. +# - Also, it does not allow to run all tests without editing the test source +# code. +# +# To use this makefile (GNU make only): +# "make": build with all predefined tests (without editing test source code) +# "make debugall": build with all predefined tests and debug code +# "make mem": perform memcheck with all tests enabled +# "make unit": build standalone (unit) test +# "make debug": build standalone test with debugging code +# +# Original 'makefile' targets can be used (test, memcheck, clean, ...) + +.PHONY: default all mem unit debug std debugtest + +default: all + +# default is to build with all predefined tests +BUILD := teststall + +include makefile + +all: CFLAGS+=-DTESTALL +all: clean test + +debugall: CFLAGS+=-DDEBUG +debugall: all + +debugtest: CFLAGS+=-DDEBUG +debugtest: test + +mem: CFLAGS+=-DTESTALL +mem: clean memcheck + +unit: CFLAGS+=-DUNIT_TEST +unit: clean std + +debug: CFLAGS+=-DUNIT_TEST -DDEBUG +debug: clean std + +debugtest: CFLAGS+=-DDEBUG +debugtest: test + +std: src/*.c src/*.h + $(CC) $(CFLAGS) src/*.c -o tests.out $(LIBS) diff --git a/c/pythagorean-triplet/README.md b/c/pythagorean-triplet/README.md new file mode 100644 index 0000000..757fb36 --- /dev/null +++ b/c/pythagorean-triplet/README.md @@ -0,0 +1,62 @@ +# Pythagorean Triplet + +A Pythagorean triplet is a set of three natural numbers, {a, b, c}, for +which, + +```text +a**2 + b**2 = c**2 +``` + +and such that, + +```text +a < b < c +``` + +For example, + +```text +3**2 + 4**2 = 9 + 16 = 25 = 5**2. +``` + +Given an input integer N, find all Pythagorean triplets for which `a + b + c = N`. + +For example, with N = 1000, there is exactly one Pythagorean triplet for which `a + b + c = 1000`: `{200, 375, 425}`. + +## Getting Started + +Make sure you have read the "Guides" section of the +[C track][c-track] on the Exercism site. This covers +the basic information on setting up the development environment expected +by the exercises. + +## Passing the Tests + +Get the first test compiling, linking and passing by following the [three +rules of test-driven development][3-tdd-rules]. + +The included makefile can be used to create and run the tests using the `test` +task. + + make test + +Create just the functions you need to satisfy any compiler errors and get the +test to fail. Then write just enough code to get the test to pass. Once you've +done that, move onto the next test. + +As you progress through the tests, take the time to refactor your +implementation for readability and expressiveness and then go on to the next +test. + +Try to use standard C99 facilities in preference to writing your own +low-level algorithms or facilities by hand. + +## Source + +Problem 9 at Project Euler [http://projecteuler.net/problem=9](http://projecteuler.net/problem=9) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[c-track]: https://exercism.io/my/tracks/c +[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd diff --git a/c/pythagorean-triplet/makefile b/c/pythagorean-triplet/makefile new file mode 100644 index 0000000..f34535a --- /dev/null +++ b/c/pythagorean-triplet/makefile @@ -0,0 +1,37 @@ +### If you wish to use extra libraries (math.h for instance), +### add their flags here (-lm in our case) in the "LIBS" variable. + +LIBS = -lm + +### +CFLAGS = -std=c99 +CFLAGS += -g +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -pedantic +CFLAGS += -Werror +CFLAGS += -Wmissing-declarations +CFLAGS += -DUNITY_SUPPORT_64 + +ASANFLAGS = -fsanitize=address +ASANFLAGS += -fno-common +ASANFLAGS += -fno-omit-frame-pointer + +.PHONY: test +test: tests.out + @./tests.out + +.PHONY: memcheck +memcheck: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(ASANFLAGS) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o memcheck.out $(LIBS) + @./memcheck.out + @echo "Memory check passed" + +.PHONY: clean +clean: + rm -rf *.o *.out *.out.dSYM + +tests.out: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o tests.out $(LIBS) diff --git a/c/pythagorean-triplet/src/pythagorean_triplet.c b/c/pythagorean-triplet/src/pythagorean_triplet.c new file mode 100644 index 0000000..a90ec3f --- /dev/null +++ b/c/pythagorean-triplet/src/pythagorean_triplet.c @@ -0,0 +1,89 @@ +#include + +#include "pythagorean_triplet.h" + +#define POOL_SIZE 10 + +/* V1: initial version + * V2: cormetic changes + * V3: add pool for realloc instead of single realloc + */ +void free_triplets(triplets_t *t) +{ + free(t->triplets); + free(t); +} + +static triplets_t *new_triplet() +{ + triplets_t *t=malloc(sizeof(triplets_t)); + if (t) { + t->count = 0; + t->pool = 0; + t->triplets = NULL; + } + return t; +} + +static triplets_t *add_triplet(triplets_t *t, const uint16_t a, const uint16_t b, + const uint16_t c) +{ + /* we may directly call add_triplet() without new_triplet. Not used here, + * but could be useful if triplets_with_sum() would return NULL when no + * solution found. + */ + if (! (t || (t = new_triplet()))) + return NULL; + + if (t->count == t->pool) { + t->pool += POOL_SIZE; + if (!(t->triplets = realloc(t->triplets, sizeof(triplets_t) + + t->pool * sizeof(triplet_t)))) { + free(t); + return NULL; + } + } + t->triplets[t->count] = (triplet_t) {a, b, c}; + t->count++; + return t; +} + +/* algorithm from : https://stackoverflow.com/a/2818750/3079831 + */ +triplets_t *triplets_with_sum(uint16_t sum) +{ + int a, b, c; + triplets_t *t; + + if (!(t = new_triplet())) + return NULL; + if (sum%2) + return t; + + for (a = 1; a <= sum/3; a++) { + for (b = a + 1; b <= sum/2; b++) { + c = sum - a - b; + if (a*a + b*b == c*c) + if (!(t = add_triplet(t, a, b, c))) + return NULL; + } + } + return t; +} + +/* See GNUmakefile below for explanation + * https://github.com/braoult/exercism/blob/master/c/templates/GNUmakefile + */ +#ifdef UNIT_TEST +int main(int ac, char **av) +{ + int arg=1; + resistor_band_t i[2]; + + for (; arg +#include + +typedef struct triplet { + uint16_t a; + uint16_t b; + uint16_t c; +} triplet_t; + +typedef struct triplets { + size_t count; + size_t pool; + triplet_t *triplets; +} triplets_t; + +triplets_t *triplets_with_sum(uint16_t sum); +void free_triplets(triplets_t *triplet); + +/* See GNUmakefile below for explanation + * https://github.com/braoult/exercism/blob/master/c/templates/GNUmakefile + */ +#if defined UNIT_TEST || defined DEBUG +#include +#include +#endif + +#ifdef TESTALL +#undef TEST_IGNORE +#define TEST_IGNORE() {} +#endif + +#endif diff --git a/c/saddle-points/GNUmakefile b/c/saddle-points/GNUmakefile new file mode 100644 index 0000000..adf038d --- /dev/null +++ b/c/saddle-points/GNUmakefile @@ -0,0 +1,51 @@ +# The original 'makefile' has a flaw: +# 1) it overrides CFLAGS +# 2) it does not pass extra "FLAGS" to $(CC) that could come from environment +# +# It means : +# - we need to edit 'makefile' for different builds (DEBUG, etc...), which is +# not practical at all. +# - Also, it does not allow to run all tests without editing the test source +# code. +# +# To use this makefile (GNU make only): +# "make": build with all predefined tests (without editing test source code) +# "make debugall": build with all predefined tests and debug code +# "make mem": perform memcheck with all tests enabled +# "make unit": build standalone (unit) test +# "make debug": build standalone test with debugging code +# +# Original 'makefile' targets can be used (test, memcheck, clean, ...) + +.PHONY: default all mem unit debug std debugtest + +default: all + +# default is to build with all predefined tests +BUILD := teststall + +include makefile + +all: CFLAGS+=-DTESTALL +all: clean test + +debugall: CFLAGS+=-DDEBUG +debugall: all + +debugtest: CFLAGS+=-DDEBUG +debugtest: test + +mem: CFLAGS+=-DTESTALL +mem: clean memcheck + +unit: CFLAGS+=-DUNIT_TEST +unit: clean std + +debug: CFLAGS+=-DUNIT_TEST -DDEBUG +debug: clean std + +debugtest: CFLAGS+=-DDEBUG +debugtest: test + +std: src/*.c src/*.h + $(CC) $(CFLAGS) src/*.c -o tests.out $(LIBS) diff --git a/c/saddle-points/README.md b/c/saddle-points/README.md new file mode 100644 index 0000000..5ebb0be --- /dev/null +++ b/c/saddle-points/README.md @@ -0,0 +1,67 @@ +# Saddle Points + +Detect saddle points in a matrix. + +So say you have a matrix like so: + +```text + 1 2 3 + |--------- +1 | 9 8 7 +2 | 5 3 2 <--- saddle point at column 1, row 2, with value 5 +3 | 6 6 7 +``` + +It has a saddle point at column 1, row 2. + +It's called a "saddle point" because it is greater than or equal to +every element in its row and less than or equal to every element in +its column. + +A matrix may have zero or more saddle points. + +Your code should be able to provide the (possibly empty) list of all the +saddle points for any given matrix. + +The matrix can have a different number of rows and columns (Non square). + +Note that you may find other definitions of matrix saddle points online, +but the tests for this exercise follow the above unambiguous definition. + +## Getting Started + +Make sure you have read the "Guides" section of the +[C track][c-track] on the Exercism site. This covers +the basic information on setting up the development environment expected +by the exercises. + +## Passing the Tests + +Get the first test compiling, linking and passing by following the [three +rules of test-driven development][3-tdd-rules]. + +The included makefile can be used to create and run the tests using the `test` +task. + + make test + +Create just the functions you need to satisfy any compiler errors and get the +test to fail. Then write just enough code to get the test to pass. Once you've +done that, move onto the next test. + +As you progress through the tests, take the time to refactor your +implementation for readability and expressiveness and then go on to the next +test. + +Try to use standard C99 facilities in preference to writing your own +low-level algorithms or facilities by hand. + +## Source + +J Dalbey's Programming Practice problems [http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html](http://users.csc.calpoly.edu/~jdalbey/103/Projects/ProgrammingPractice.html) + +## Submitting Incomplete Solutions +It's possible to submit an incomplete solution so you can see how others have completed the exercise. + +[c-track]: https://exercism.io/my/tracks/c +[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd diff --git a/c/saddle-points/makefile b/c/saddle-points/makefile new file mode 100644 index 0000000..f34535a --- /dev/null +++ b/c/saddle-points/makefile @@ -0,0 +1,37 @@ +### If you wish to use extra libraries (math.h for instance), +### add their flags here (-lm in our case) in the "LIBS" variable. + +LIBS = -lm + +### +CFLAGS = -std=c99 +CFLAGS += -g +CFLAGS += -Wall +CFLAGS += -Wextra +CFLAGS += -pedantic +CFLAGS += -Werror +CFLAGS += -Wmissing-declarations +CFLAGS += -DUNITY_SUPPORT_64 + +ASANFLAGS = -fsanitize=address +ASANFLAGS += -fno-common +ASANFLAGS += -fno-omit-frame-pointer + +.PHONY: test +test: tests.out + @./tests.out + +.PHONY: memcheck +memcheck: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(ASANFLAGS) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o memcheck.out $(LIBS) + @./memcheck.out + @echo "Memory check passed" + +.PHONY: clean +clean: + rm -rf *.o *.out *.out.dSYM + +tests.out: test/*.c src/*.c src/*.h + @echo Compiling $@ + @$(CC) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o tests.out $(LIBS) diff --git a/c/saddle-points/src/saddle_points.c b/c/saddle-points/src/saddle_points.c new file mode 100644 index 0000000..0daf379 --- /dev/null +++ b/c/saddle-points/src/saddle_points.c @@ -0,0 +1,142 @@ +#include + +#include "saddle_points.h" + +#define POOL_SIZE 10 +#define MAX_STACK 1024 + +static saddle_point_t stack[MAX_STACK]; +static int stack_pos; + +static int push_maybe(uint8_t max, uint8_t val, uint8_t r, uint8_t c) +{ + if (val > max) { /* new max value */ + max = val; + stack_pos = 0; + } + if (val == max) { + if (stack_pos == MAX_STACK) /* stack overflow */ + return -1; + stack[stack_pos] = (saddle_point_t) {r, c}; + stack_pos++; + } + return max; +} + +static saddle_point_t *pop() +{ + if (!stack_pos) + return NULL; + stack_pos--; + return &stack[stack_pos]; +} + +static saddle_points_t *new_saddle_points() +{ + saddle_points_t *t = malloc(sizeof(saddle_points_t)); + if (t) { + t->count = 0; + t->pool = 0; + t->points = NULL; + } + return t; +} + +static saddle_points_t *add_point(saddle_points_t *saddle, const uint8_t r, + const uint8_t c) +{ + /* we may directly call add_point() without new_saddle_points(). + * Not used here, but could be useful if triplets_with_sum() should + * return NULL when no solution found, instead of empty set. + */ + if (! (saddle || (saddle = new_saddle_points()))) + return NULL; + + if (saddle->count == saddle->pool) { + saddle->pool += POOL_SIZE; + if (!(saddle->points = realloc(saddle->points, sizeof(saddle) + + saddle->pool * sizeof(saddle_point_t)))) { + free(saddle); + return NULL; + } + } + saddle->points[saddle->count++] = (saddle_point_t) {r, c}; + return saddle; +} + +saddle_points_t *saddle_points(uint8_t ro, uint8_t co, uint8_t m[ro][co]) +{ + saddle_points_t *saddle; + saddle_point_t *possible; + + if (!(saddle = new_saddle_points())) + return NULL; + /* find largest value in each row */ + for (int16_t row = 0; row < ro; ++row) { + int max = 0; + for (int16_t col = 0; col < co; ++col) { + /* partial result (stack overflow) */ + if ((max = push_maybe(max, m[row][col], row, col)) < 0) + return saddle; + } + /* we have now the largest row values on stack, we check if they are + * saddle points (= lowest in their columns) + */ + while ((possible = pop())) { + for (int r1 = 0; r1 < ro; ++r1) { + if (m[possible->row][possible->column] > m[r1][possible->column]) + goto not_saddle; + } + saddle = add_point(saddle, possible->row+1, possible->column+1); + not_saddle: ; + } + } + return saddle; +} + +void free_saddle_points(saddle_points_t *s) +{ + free(s->points); + free(s); +} + +/* See GNUmakefile below for explanation + * https://github.com/braoult/exercism/blob/master/c/templates/GNUmakefile + */ +#ifdef UNIT_TEST +#include + +int main(int ac, char **av) +{ + int arg; + uint8_t row, col; + saddle_points_t *s; + + if (ac > 3) { + row=atoi(av[1]); + col=atoi(av[2]); + arg=3; + if ((ac-3) == row*col) { + uint8_t array[row][col]; + uint8_t *p=(uint8_t *)array; + printf("row=%d col=%d ac=%d arg=%d\n", row, col, ac, arg); + while (argcount); + for (unsigned i=0; icount; ++i) { + printf("%u: (%u, %u)\n", i, s->points[i].column, + s->points[i].row); + + } + } + } +} +#endif diff --git a/c/saddle-points/src/saddle_points.h b/c/saddle-points/src/saddle_points.h new file mode 100644 index 0000000..fe7267b --- /dev/null +++ b/c/saddle-points/src/saddle_points.h @@ -0,0 +1,36 @@ +#ifndef SADDLE_POINTS_H +#define SADDLE_POINTS_H + +#include +#include + +typedef struct { + uint8_t row; + uint8_t column; +} saddle_point_t; + +typedef struct { + size_t count; + size_t pool; + saddle_point_t *points; +} saddle_points_t; + +extern saddle_points_t *saddle_points(uint8_t rows, uint8_t columns, + uint8_t matrix[rows][columns]); + +extern void free_saddle_points(saddle_points_t *saddle_point); + +/* See GNUmakefile below for explanation + * https://github.com/braoult/exercism/blob/master/c/templates/GNUmakefile + */ +#if defined UNIT_TEST || defined DEBUG +#include +#include +#endif + +#ifdef TESTALL +#undef TEST_IGNORE +#define TEST_IGNORE() {} +#endif + +#endif