C: pythagorean-triplet and saddle-point

This commit is contained in:
2021-08-25 20:35:55 +02:00
parent b200831b51
commit 5a35db2396
10 changed files with 606 additions and 0 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -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)

View File

@@ -0,0 +1,89 @@
#include <malloc.h>
#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<ac-1; ++arg, ++arg) {
*i=atoi(av[arg]);
*(i+1)=atoi(av[arg+1]);
printf("color(%d, %d)=%d\n", i[0], i[1], color_code(i));
}
}
#endif

View File

@@ -0,0 +1,34 @@
#ifndef PYTHAGOREAN_TRIPLET
#define PYTHAGOREAN_TRIPLET
#include <stdlib.h>
#include <stdint.h>
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 <stdio.h>
#include <stdlib.h>
#endif
#ifdef TESTALL
#undef TEST_IGNORE
#define TEST_IGNORE() {}
#endif
#endif

View File

@@ -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)

67
c/saddle-points/README.md Normal file
View File

@@ -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

37
c/saddle-points/makefile Normal file
View File

@@ -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)

View File

@@ -0,0 +1,142 @@
#include <stdbool.h>
#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 <stdio.h>
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 (arg<ac)
*p++=atoi(av[arg++]);
for (int r=0; r<row; ++r) {
for (int c=0; c<col; ++c) {
printf("%d ", array[r][c]);
}
printf("\n");
}
s = saddle_points(row, col, array);
printf("saddles: %lu\n", s->count);
for (unsigned i=0; i<s->count; ++i) {
printf("%u: (%u, %u)\n", i, s->points[i].column,
s->points[i].row);
}
}
}
}
#endif

View File

@@ -0,0 +1,36 @@
#ifndef SADDLE_POINTS_H
#define SADDLE_POINTS_H
#include <stdint.h>
#include <stdlib.h>
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 <stdio.h>
#include <stdlib.h>
#endif
#ifdef TESTALL
#undef TEST_IGNORE
#define TEST_IGNORE() {}
#endif
#endif