Compare commits

...

16 Commits

47 changed files with 2718 additions and 179 deletions

1
.gitignore vendored
View File

@@ -2,6 +2,7 @@ core
*.out
*_test.sh
test/
test-framework/
users/
.exercism
.projectile

View File

@@ -1 +1 @@
My solutions to some [exercism](https://exercism.io/) exercises.
My solutions to some [exercism](https://exercism.org/) exercises.

56
c/linked-list/GNUmakefile Normal file
View File

@@ -0,0 +1,56 @@
# 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):
# 1) copy it into exercise directory
# 2) add ex.h to exercise include file
# 3) add ex.c to exercise source code, and create a suitable main function
# 4) use make with one of the following targets :
# all: compile and run all predefined tests.
# nowarn: compile with no -Werror, and run all predefined tests
# debug: compile with -DDEBUG and run all predefined tests
# mem: perform memcheck with all tests enabled
# unit: build standalone (unit) bimary
# unitnowarn: build standalone (unit) binary with -Werror disabled
# unitdebug: build standalone binary with -DDEBUG
#
# Original 'makefile' targets can be used (test, memcheck, clean, ...)
.PHONY: default all nowarn debug mem unit unitnowarn unitdebug standalone
default: all
ALLSOURCES:=$(wildcard ./*.c)
TESTSOURCES:=$(wildcard ./test_*.c)
SRC:=$(filter-out $(TESTSOURCES),$(ALLSOURCES))
include makefile
all: CFLAGS+=-DTESTALL
all: clean test
nowarn: CFLAGS:=$(filter-out -Werror,$(CFLAGS))
nowarn: clean all
debug: CFLAGS+=-DDEBUG
debug: all
mem: CFLAGS+=-DTESTALL
mem: clean memcheck
unitnowarn: CFLAGS:=$(filter-out -Werror,$(CFLAGS))
unitnowarn: clean unit
unitdebug: CFLAGS+=-DDEBUG
unitdebug: clean unit
unit: CFLAGS+=-DUNIT_TEST
unit: *.c *.h
$(CC) $(CFLAGS) $(SRC) -o tests.out $(LIBS)

63
c/linked-list/HELP.md Normal file
View File

@@ -0,0 +1,63 @@
# Help
## Running 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.
```console
$ 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.
[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd
## Submitting your solution
You can submit your solution using the `exercism submit linked_list.c linked_list.h` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [C track's documentation](https://exercism.org/docs/tracks/c)
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
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.
## Submitting Incomplete Solutions
If you are struggling with a particular exercise, it is possible to submit an incomplete solution so you can see how others have completed the exercise.
## Resources
To get help if having trouble, you can use the following resources:
- [StackOverflow][] can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.
- [CPPReference][] can be used to look up information on C concepts, operators, types, standard library functions and more.
- [TutorialsPoint][] has similar content as CPPReference in its C programming section.
- [The C Programming][K&R] book by K&R is the original source of the language and is still useful today.
[c-track]: https://exercism.io/my/tracks/c
[stackoverflow]: http://stackoverflow.com/questions/tagged/c
[cppreference]: https://en.cppreference.com/w/c
[tutorialspoint]: https://www.tutorialspoint.com/cprogramming/
[K&R]: https://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628/

50
c/linked-list/README.md Normal file
View File

@@ -0,0 +1,50 @@
# Linked List
Welcome to Linked List on Exercism's C Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
Implement a doubly linked list.
Like an array, a linked list is a simple linear data structure. Several
common data types can be implemented using linked lists, like queues,
stacks, and associative arrays.
A linked list is a collection of data elements called *nodes*. In a
*singly linked list* each node holds a value and a link to the next node.
In a *doubly linked list* each node also holds a link to the previous
node.
You will write an implementation of a doubly linked list. Implement a
Node to hold a value and pointers to the next and previous nodes. Then
implement a List which holds references to the first and last node and
offers an array-like interface for adding and removing items:
* `push` (*insert value at back*);
* `pop` (*remove value at back*);
* `shift` (*remove value at front*).
* `unshift` (*insert value at front*);
To keep your implementation simple, the tests will not cover error
conditions. Specifically: `pop` or `shift` will never be called on an
empty list.
If you want to know more about linked lists, check [Wikipedia](https://en.wikipedia.org/wiki/Linked_list).
## Source
### Created by
- @wolf99
### Contributed to by
- @patricksjackson
- @QLaille
- @ryanplusplus
- @siebenschlaefer
### Based on
Classic computer science topic

45
c/linked-list/br-common.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef __BR_COMMON_H
#define __BR_COMMON_H
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 30
*
* __has_attribute is supported on gcc >= 5, clang >= 2.9 and icc >= 17.
* For other compilers, simple implementation (for __falltrough__ only)
*/
#ifndef __has_attribute
# define __has_attribute(x) __GCC4_has_attribute_##x
# define __GCC4_has_attribute___assume_aligned__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___copy__ 0
# define __GCC4_has_attribute___designated_init__ 0
# define __GCC4_has_attribute___externally_visible__ 1
# define __GCC4_has_attribute___no_caller_saved_registers__ 0
# define __GCC4_has_attribute___noclone__ 1
# define __GCC4_has_attribute___nonstring__ 0
# define __GCC4_has_attribute___no_sanitize_address__ (__GNUC_MINOR__ >= 8)
# define __GCC4_has_attribute___no_sanitize_undefined__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___fallthrough__ 0
# define __GCC4_has_attribute___fallthrough__ 0
#endif
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 200
*/
#if __has_attribute(__fallthrough__)
# define fallthrough __attribute__((__fallthrough__))
#else
# define fallthrough do {} while (0) /* fallthrough */
#endif
/* 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 /* __BR_COMMON_H */

138
c/linked-list/linked_list.c Normal file
View File

@@ -0,0 +1,138 @@
#include <malloc.h>
#include "list.h"
#include "linked_list.h"
/* How it works :
*
* The usual way to use linked lists is to have a list_head whith
* 'next' pointing to first node, and 'prev' pointing to last node.
* Each node has also 'prev' and 'next' pointers.
*
* Here, list_head points to the node own list_head structure,
* and each node list_head points to next/previous node list_head.
*
* Advantage: We don't need to manipulate the pointers, all lists
* use the same code, independently of the object (node) structure.
*/
struct list_node {
ll_data_t data;
struct list_head list;
};
/* duplicate of list_head struct
*/
struct list {
struct list_head *next, *prev;
};
/* constructs a new (empty) list
*/
struct list *list_create()
{
struct list_head *list;
if (!(list=malloc(sizeof (*list))))
return NULL;
INIT_LIST_HEAD(list);
return (struct list *) list;
}
/* counts the items on a list
*/
size_t list_count(const struct list *list_head)
{
size_t len = 0;
struct list_head *p;
list_for_each(p, (struct list_head *)list_head)
len++;
return len;
}
/* inserts item at back of a list
*/
void list_push(const struct list *list, const ll_data_t item_data)
{
struct list_node *p;
if ((p=malloc(sizeof(*p)))) {
p->data = item_data;
list_add_tail(&p->list, (struct list_head*)list);
}
}
/* deletes an element
*/
static ll_data_t _list_del(const struct list_head *list)
{
struct list_node *node;
ll_data_t data;
node = list_entry(list, struct list_node, list);
data = node->data;
list_del(&node->list);
free(node);
return data;
}
/* removes item from back of a list
*/
ll_data_t list_pop(const struct list *list)
{
return list_empty((struct list_head*)list) ? -1 : _list_del(list->prev);
}
/* inserts item at front of a list
*/
void list_unshift(const struct list *list, const ll_data_t item_data)
{
struct list_node *p;
if ((p=malloc(sizeof(*p)))) {
p->data = item_data;
list_add(&p->list, (struct list_head*)list);
}
}
/* removes item from front of a list
*/
ll_data_t list_shift(const struct list *list)
{
return list_empty((struct list_head*)list) ? -1 : _list_del(list->next);
}
/* finds a element that matches data
*/
static struct list_node *_node_find(const struct list *list, const ll_data_t data)
{
struct list_node *p;
list_for_each_entry(p, (struct list_head *)list, list) {
if (p->data == data)
return p;
}
return NULL;
}
/* deletes a node that holds the matching data
*/
void list_delete(const struct list *list, const ll_data_t data)
{
struct list_node *p;
if ((p = _node_find(list, data)))
_list_del(&p->list);
}
/* destroys an entire list
* list will be a dangling pointer after calling this method on it
*/
void list_destroy(struct list *list)
{
struct list_head *cur, *tmp;
list_for_each_safe(cur, tmp, (struct list_head *)list)
_list_del(cur);
free(list);
}

View File

@@ -0,0 +1,36 @@
#ifndef LINKED_LIST_H
#define LINKED_LIST_H
#include <stddef.h>
typedef int ll_data_t;
struct list;
// constructs a new (empty) list
struct list *list_create(void);
// counts the items on a list
size_t list_count(const struct list *list);
// inserts item at back of a list
void list_push(const struct list *list, const ll_data_t item_data);
// removes item from back of a list
ll_data_t list_pop(const struct list *list);
// inserts item at front of a list
void list_unshift(const struct list *list, const ll_data_t item_data);
// removes item from front of a list
ll_data_t list_shift(const struct list *list);
// deletes a node that holds the matching data
void list_delete(const struct list *list, const ll_data_t data);
// destroys an entire list
// list will be a dangling pointer after calling this method on it
void list_destroy(struct list *list);
#endif
#include "br-common.h"

229
c/linked-list/list.h Normal file
View File

@@ -0,0 +1,229 @@
/* SPDX-License-Identifier: GPL-2.0 */
/* stripped down version of my adaptation of Linux kernel lists management :
* https://github.com/braoult/Tools/blob/master/C/list.h
*/
#ifndef __LIST_H
#define __LIST_H
#include <stddef.h>
#include <stdbool.h>
/************ originally in <include/linux/types.h> */
struct list_head {
struct list_head *next, *prev;
};
/************ originally in <include/linux/poison.h> */
#define LIST_POISON1 ((void *) 0x100)
#define LIST_POISON2 ((void *) 0x122)
/************ originally in <include/linux/kernel.h> */
#define container_of(ptr, type, member) \
((type *)((char *)(ptr) - offsetof(type, member)))
/*
* Circular doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
/**
* INIT_LIST_HEAD - Initialize a list_head structure
* @list: list_head structure to be initialized.
*
* Initializes the list_head to point to itself. If it is a list header,
* the result is an empty list.
*/
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head * prev, struct list_head * next)
{
next->prev = prev;
prev->next = next;
}
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void list_del(struct list_head *entry)
{
__list_del_entry(entry);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_last_entry - get the last element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
/**
* list_next_entry - get the next element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, __typeof__(*(pos)), member)
/**
* list_prev_entry - get the prev element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_prev_entry(pos, member) \
list_entry((pos)->member.prev, __typeof__(*(pos)), member)
/**
* list_for_each - iterate over a list
* @pos: the &struct list_head to use as a loop cursor.
* @head: the head for your list.
*/
#define list_for_each(pos, head) \
for (pos = (head)->next; pos != (head); pos = pos->next)
/**
* list_for_each_safe - iterate over a list safe against removal of list entry
* @pos: the &struct list_head to use as a loop cursor.
* @n: another &struct list_head to use as temporary storage
* @head: the head for your list.
*/
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
/**
* list_entry_is_head - test if the entry points to the head of the list
* @pos: the type * to cursor
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_entry_is_head(pos, head, member) \
(&pos->member == (head))
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, __typeof__(*pos), member); \
!list_entry_is_head(pos, head, member); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, __typeof__(*pos), member), \
n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
#endif /* __LIST_H */

37
c/linked-list/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: ./*.c ./*.h
@echo Compiling $@
@$(CC) $(ASANFLAGS) $(CFLAGS) test-framework/unity.c ./*.c -o memcheck.out $(LIBS)
@./memcheck.out
@echo "Memory check passed"
.PHONY: clean
clean:
rm -rf *.o *.out *.out.dSYM
tests.out: ./*.c ./*.h
@echo Compiling $@
@$(CC) $(CFLAGS) test-framework/unity.c ./*.c -o tests.out $(LIBS)

View File

@@ -0,0 +1,243 @@
#include <stddef.h>
#include "test-framework/unity.h"
#include "linked_list.h"
struct list *list = NULL;
void setUp(void)
{
list = list_create();
}
void tearDown(void)
{
if (list) {
list_destroy(list);
list = NULL;
}
}
static void test_pop_gets_element_from_the_list(void)
{
list_push(list, 7);
TEST_ASSERT_EQUAL(7, list_pop(list));
}
static void test_push_pop_respectively_add_remove_at_the_end_of_the_list(void)
{
TEST_IGNORE(); // delete this line to run test
list_push(list, 11);
list_push(list, 13);
TEST_ASSERT_EQUAL(13, list_pop(list));
TEST_ASSERT_EQUAL(11, list_pop(list));
}
static void test_shift_gets_an_element_from_the_list(void)
{
TEST_IGNORE();
list_push(list, 17);
TEST_ASSERT_EQUAL(17, list_shift(list));
}
static void test_shift_gets_first_element_from_the_list(void)
{
TEST_IGNORE();
list_push(list, 23);
list_push(list, 5);
TEST_ASSERT_EQUAL(23, list_shift(list));
TEST_ASSERT_EQUAL(5, list_shift(list));
}
static void test_unshift_adds_element_at_start_of_the_list(void)
{
TEST_IGNORE();
list_unshift(list, 23);
list_unshift(list, 5);
TEST_ASSERT_EQUAL(5, list_shift(list));
TEST_ASSERT_EQUAL(23, list_shift(list));
}
static void test_pop_push_shift_and_unshift_can_be_used_in_any_order(void)
{
TEST_IGNORE();
list_push(list, 1);
list_push(list, 2);
TEST_ASSERT_EQUAL(2, list_pop(list));
list_push(list, 3);
TEST_ASSERT_EQUAL(1, list_shift(list));
list_unshift(list, 4);
list_push(list, 5);
TEST_ASSERT_EQUAL(4, list_shift(list));
TEST_ASSERT_EQUAL(5, list_pop(list));
TEST_ASSERT_EQUAL(3, list_shift(list));
}
static void test_count_an_empty_list(void)
{
TEST_IGNORE();
TEST_ASSERT_EQUAL(0, list_count(list));
}
static void test_count_a_list_with_items(void)
{
TEST_IGNORE();
list_push(list, 37);
list_push(list, 1);
TEST_ASSERT_EQUAL(2, list_count(list));
}
static void test_count_is_correct_after_mutation(void)
{
TEST_IGNORE();
list_push(list, 31);
TEST_ASSERT_EQUAL(1, list_count(list));
list_unshift(list, 43);
TEST_ASSERT_EQUAL(2, list_count(list));
list_shift(list);
TEST_ASSERT_EQUAL(1, list_count(list));
list_pop(list);
TEST_ASSERT_EQUAL(0, list_count(list));
}
static void test_popping_to_empty_does_not_break_the_list(void)
{
TEST_IGNORE();
list_push(list, 41);
list_push(list, 59);
list_pop(list);
list_pop(list);
list_push(list, 47);
TEST_ASSERT_EQUAL(1, list_count(list));
TEST_ASSERT_EQUAL(47, list_pop(list));
}
static void test_shifting_to_empty_does_not_break_the_list(void)
{
TEST_IGNORE();
list_push(list, 41);
list_push(list, 59);
list_shift(list);
list_shift(list);
list_push(list, 47);
TEST_ASSERT_EQUAL(1, list_count(list));
TEST_ASSERT_EQUAL(47, list_shift(list));
}
static void test_deletes_the_only_element(void)
{
TEST_IGNORE();
list_push(list, 61);
list_delete(list, 61);
TEST_ASSERT_EQUAL(0, list_count(list));
}
static void
test_deletes_the_element_with_the_specified_value_from_the_list(void)
{
TEST_IGNORE();
list_push(list, 71);
list_push(list, 83);
list_push(list, 79);
list_delete(list, 83);
TEST_ASSERT_EQUAL(2, list_count(list));
TEST_ASSERT_EQUAL(79, list_pop(list));
TEST_ASSERT_EQUAL(71, list_shift(list));
}
static void
test_deletes_the_element_with_the_specified_value_from_the_list_reassigns_tail
(void) {
TEST_IGNORE();
list_push(list, 71);
list_push(list, 83);
list_push(list, 79);
list_delete(list, 83);
TEST_ASSERT_EQUAL(2, list_count(list));
TEST_ASSERT_EQUAL(79, list_pop(list));
TEST_ASSERT_EQUAL(71, list_pop(list));
}
static void
test_deletes_the_element_with_the_specified_value_from_the_list_reassigns_head
(void) {
TEST_IGNORE();
list_push(list, 71);
list_push(list, 83);
list_push(list, 79);
list_delete(list, 83);
TEST_ASSERT_EQUAL(2, list_count(list));
TEST_ASSERT_EQUAL(71, list_shift(list));
TEST_ASSERT_EQUAL(79, list_shift(list));
}
static void test_deletes_the_first_of_two_elements(void)
{
TEST_IGNORE();
list_push(list, 97);
list_push(list, 101);
list_delete(list, 97);
TEST_ASSERT_EQUAL(1, list_count(list));
TEST_ASSERT_EQUAL(101, list_pop(list));
}
static void test_deletes_the_second_of_two_elements(void)
{
TEST_IGNORE();
list_push(list, 97);
list_push(list, 101);
list_delete(list, 101);
TEST_ASSERT_EQUAL(1, list_count(list));
TEST_ASSERT_EQUAL(97, list_pop(list));
}
static void
test_delete_does_not_modify_the_list_if_the_element_is_not_found(void)
{
TEST_IGNORE();
list_push(list, 89);
list_delete(list, 103);
TEST_ASSERT_EQUAL(1, list_count(list));
}
static void test_deletes_only_the_first_occurrence(void)
{
TEST_IGNORE();
list_push(list, 73);
list_push(list, 9);
list_push(list, 9);
list_push(list, 107);
list_delete(list, 9);
TEST_ASSERT_EQUAL(3, list_count(list));
TEST_ASSERT_EQUAL(107, list_pop(list));
TEST_ASSERT_EQUAL(9, list_pop(list));
TEST_ASSERT_EQUAL(73, list_pop(list));
}
int main(void)
{
UnityBegin("test_linked_list.c");
RUN_TEST(test_pop_gets_element_from_the_list);
RUN_TEST(test_push_pop_respectively_add_remove_at_the_end_of_the_list);
RUN_TEST(test_shift_gets_an_element_from_the_list);
RUN_TEST(test_shift_gets_first_element_from_the_list);
RUN_TEST(test_unshift_adds_element_at_start_of_the_list);
RUN_TEST(test_pop_push_shift_and_unshift_can_be_used_in_any_order);
RUN_TEST(test_count_an_empty_list);
RUN_TEST(test_count_a_list_with_items);
RUN_TEST(test_count_is_correct_after_mutation);
RUN_TEST(test_popping_to_empty_does_not_break_the_list);
RUN_TEST(test_shifting_to_empty_does_not_break_the_list);
RUN_TEST(test_deletes_the_only_element);
RUN_TEST(test_deletes_the_element_with_the_specified_value_from_the_list);
RUN_TEST
(test_deletes_the_element_with_the_specified_value_from_the_list_reassigns_tail);
RUN_TEST
(test_deletes_the_element_with_the_specified_value_from_the_list_reassigns_head);
RUN_TEST(test_deletes_the_first_of_two_elements);
RUN_TEST(test_deletes_the_second_of_two_elements);
RUN_TEST(test_delete_does_not_modify_the_list_if_the_element_is_not_found);
RUN_TEST(test_deletes_only_the_first_occurrence);
return UnityEnd();
}

View File

@@ -27,6 +27,10 @@
default: all
ALLSOURCES:=$(wildcard ./*.c)
TESTSOURCES:=$(wildcard ./test_*.c)
SRC:=$(filter-out $(TESTSOURCES),$(ALLSOURCES))
include makefile
all: CFLAGS+=-DTESTALL
@@ -48,5 +52,5 @@ unitdebug: CFLAGS+=-DDEBUG
unitdebug: clean unit
unit: CFLAGS+=-DUNIT_TEST
unit: src/*.c src/*.h
$(CC) $(CFLAGS) src/*.c -o tests.out $(LIBS)
unit: *.c *.h
$(CC) $(CFLAGS) $(SRC) -o tests.out $(LIBS)

63
c/phone-number/HELP.md Normal file
View File

@@ -0,0 +1,63 @@
# Help
## Running 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.
```console
$ 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.
[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd
## Submitting your solution
You can submit your solution using the `exercism submit phone_number.c phone_number.h` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [C track's documentation](https://exercism.org/docs/tracks/c)
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
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.
## Submitting Incomplete Solutions
If you are struggling with a particular exercise, it is possible to submit an incomplete solution so you can see how others have completed the exercise.
## Resources
To get help if having trouble, you can use the following resources:
- [StackOverflow][] can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.
- [CPPReference][] can be used to look up information on C concepts, operators, types, standard library functions and more.
- [TutorialsPoint][] has similar content as CPPReference in its C programming section.
- [The C Programming][K&R] book by K&R is the original source of the language and is still useful today.
[c-track]: https://exercism.io/my/tracks/c
[stackoverflow]: http://stackoverflow.com/questions/tagged/c
[cppreference]: https://en.cppreference.com/w/c
[tutorialspoint]: https://www.tutorialspoint.com/cprogramming/
[K&R]: https://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628/

View File

@@ -1,5 +1,10 @@
# Phone Number
Welcome to Phone Number on Exercism's C Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
Clean up user-entered phone numbers so that they can be sent SMS messages.
The **North American Numbering Plan (NANP)** is a telephone numbering system used by many countries in North America like the United States, Canada or Bermuda. All NANP-countries share the same international country code: `1`.
@@ -28,40 +33,24 @@ should all produce the output
**Note:** As this exercise only deals with telephone numbers used in NANP-countries, only 1 is considered a valid country code.
## 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
Event Manager by JumpstartLab [http://tutorials.jumpstartlab.com/projects/eventmanager.html](http://tutorials.jumpstartlab.com/projects/eventmanager.html)
### Created by
## Submitting Incomplete Solutions
It's possible to submit an incomplete solution so you can see how others have completed the exercise.
- @RealBarrettBrown
[c-track]: https://exercism.io/my/tracks/c
[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd
### Contributed to by
- @bcc32
- @Gamecock
- @gea-migration
- @h-3-0
- @mikewalker
- @patricksjackson
- @QLaille
- @ryanplusplus
- @wolf99
### Based on
Event Manager by JumpstartLab - http://tutorials.jumpstartlab.com/projects/eventmanager.html

View File

@@ -0,0 +1,45 @@
#ifndef __BR_COMMON_H
#define __BR_COMMON_H
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 30
*
* __has_attribute is supported on gcc >= 5, clang >= 2.9 and icc >= 17.
* For other compilers, simple implementation (for __falltrough__ only)
*/
#ifndef __has_attribute
# define __has_attribute(x) __GCC4_has_attribute_##x
# define __GCC4_has_attribute___assume_aligned__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___copy__ 0
# define __GCC4_has_attribute___designated_init__ 0
# define __GCC4_has_attribute___externally_visible__ 1
# define __GCC4_has_attribute___no_caller_saved_registers__ 0
# define __GCC4_has_attribute___noclone__ 1
# define __GCC4_has_attribute___nonstring__ 0
# define __GCC4_has_attribute___no_sanitize_address__ (__GNUC_MINOR__ >= 8)
# define __GCC4_has_attribute___no_sanitize_undefined__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___fallthrough__ 0
# define __GCC4_has_attribute___fallthrough__ 0
#endif
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 200
*/
#if __has_attribute(__fallthrough__)
# define fallthrough __attribute__((__fallthrough__))
#else
# define fallthrough do {} while (0); /* fallthrough */
#endif
/* 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 /* __BR_COMMON_H */

View File

@@ -13,6 +13,20 @@ CFLAGS += -Werror
CFLAGS += -Wmissing-declarations
CFLAGS += -DUNITY_SUPPORT_64
# detect compiler.
REALCC=$(realpath $(shell which $(CC)))
CC_VERSION_TEXT=$(shell $(REALCC) --version 2>/dev/null | head -n 1)
# fix discrepancies in compilers warnings. Only gcc and clang for now.
ifneq ($(findstring clang,$(CC_VERSION_TEXT)),)
CFLAGS += -Wimplicit-fallthrough
else
ifneq ($(findstring gcc,$(CC_VERSION_TEXT)),)
CFLAGS := $(filter-out -Wimplicit-fallthrough%,$(CFLAGS))
CFLAGS += -Wimplicit-fallthrough=5
endif
endif
ASANFLAGS = -fsanitize=address
ASANFLAGS += -fno-common
ASANFLAGS += -fno-omit-frame-pointer
@@ -22,9 +36,9 @@ test: tests.out
@./tests.out
.PHONY: memcheck
memcheck: test/*.c src/*.c src/*.h
memcheck: ./*.c ./*.h
@echo Compiling $@
@$(CC) $(ASANFLAGS) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o memcheck.out $(LIBS)
$(CC) $(ASANFLAGS) $(CFLAGS) test-framework/unity.c ./*.c -o memcheck.out $(LIBS)
@./memcheck.out
@echo "Memory check passed"
@@ -32,6 +46,6 @@ memcheck: test/*.c src/*.c src/*.h
clean:
rm -rf *.o *.out *.out.dSYM
tests.out: test/*.c src/*.c src/*.h
tests.out: ./*.c ./*.h
@echo Compiling $@
@$(CC) $(CFLAGS) src/*.c test/vendor/unity.c test/*.c -o tests.out $(LIBS)
$(CC) $(CFLAGS) test-framework/unity.c ./*.c -o tests.out $(LIBS)

View File

@@ -0,0 +1,91 @@
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <stdint.h>
#include "phone_number.h"
#define LEN_NUM 10
#define LPAREN '('
/* this version is likely not very stable, due to poor scanf() capabilities
* I made it to offer an option to traditional strtok() or manual string
* parsing.
* Examples:
* (1 (222-333.4444
* 1)))))) 222))) ...---... 333 ...---... 4444
* are both valid here, when they surely should not :-)
*/
static char *scan="%m[+(0-9]%*[()-. ]%m[0-9]%*[)-. ]%m[0-9]%*[-. ]%m[0-9]";
char *phone_number_clean(const char *input)
{
char *sn[4];
uint64_t num[4];
uint64_t *p = &*num;
int nmatch;
char *res;
if (!(res = malloc(LEN_NUM+1)))
return NULL;
memset(res, '0', LEN_NUM);
*(res+LEN_NUM) = 0;
nmatch = sscanf(input, scan, &sn[0], &sn[1], &sn[2], &sn[3]);
for (int i=0; i<nmatch; ++i) {
*(p+i) = atol(*sn[i] == LPAREN? sn[i]+1: sn[i]);
free(sn[i]); /* due to scanf %m */
}
switch (nmatch) {
case 2:
/* maybe 2 could be valid, like 333 3334444 ?
*/
case 0:
return res;
case 1: /* full number */
if (*p > 10000000000) /* 1 000 000 0000 */
*p -= 10000000000;
if (*p > 9999999999 || /* 999 999 9999 */
*p < 2000000000) /* 200 000 0000 */
return res;
break;
case 4: /* country code */
if (*p != 1)
return res;
p++; /* go to area number */
fallthrough; /* only gcc>=7 & clang>=12 */
case 3: /* start with area number */
if (*p < 200 || *p > 999 ||
*(p+1) < 200 || *(p+1) > 999 ||
*(p+2) > 9999)
return res;
break;
}
/* we don't care if some num has random value here (initialized), snprintf
* will consume only what is needed to fill the number
*/
snprintf(res, LEN_NUM+1, "%ld%ld%ld", *p, *(p+1), *(p+2));
return res;
}
/* 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;
char *res;
for (arg=1; arg<ac; ++arg) {
res=phone_number_clean(av[arg]);
printf("orig = [%s]\n", av[arg]);
printf("\t-> [%s]\n", res);
free(res);
}
}
#endif

View File

@@ -0,0 +1,10 @@
#ifndef PHONE_NUMBER_H
#define PHONE_NUMBER_H
#define NUMBER_LENGTH 10
char *phone_number_clean(const char *input);
#endif
#include "br-common.h"

View File

@@ -1,79 +0,0 @@
#include <stdio.h>
#include <stdlib.h>
#include <malloc.h>
#include <string.h>
#include <stdint.h>
#include "phone_number.h"
#define LEN_NUM 10
#define LPAREN '('
/* this version is likely not very stable, due to poor scanf() capabilities
* I made it to offer an option to traditional strtok() of manual string
* parsing.
*/
char *phone_number_clean(const char *input)
{
char *scan="%m[+(0-9]%*[()-. ]%m[0-9]%*[()-. ]%m[0-9]%*[-. ]%m[0-9]";
char *sn[4];
int64_t num[4];
int64_t *p = &num[0];
int nmatch;
char *res;
if (!(res = malloc(LEN_NUM+1)))
return NULL;
memset(res, '0', LEN_NUM);
*(res+LEN_NUM) = 0;
nmatch = sscanf(input, scan, &sn[0], &sn[1], &sn[2], &sn[3]);
for (int i=0; i<nmatch; ++i) {
num[i] = atol(*sn[i] == LPAREN? sn[i]+1: sn[i]);
free(sn[i]);
}
switch (nmatch) {
case 2:
case 0:
return res;
case 1:
if (num[0] > 10000000000)
num[0] -= 10000000000;
if (num[0] > 9999999999 || num[0] < 2000000000)
return res;
break;
case 4: /* area */
if (num[0] != 1)
return res;
p++;
fallthrough;
case 3: /* last 3 numbers */
if (p[0] < 200 || p[0] > 999 || p[1] < 200 || p[1] > 999 ||
p[2] < 0 || p[2] > 9999)
return res;
break;
}
snprintf(res, LEN_NUM+1, "%ld%ld%ld", *p, *(p+1), *(p+2));
return res;
}
/* 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;
char *res;
for (arg=1; arg<ac; ++arg) {
res=phone_number_clean(av[arg]);
printf("orig = [%s]\n", av[arg]);
printf("\t-> [%s]\n", res);
free(res);
}
}
#endif

View File

@@ -1,32 +0,0 @@
#ifndef PHONE_NUMBER_H
#define PHONE_NUMBER_H
#define NUMBER_LENGTH 10
char *phone_number_clean(const char *input);
/* from :
* https://github.com/torvalds/linux/blob/master/include/linux/compiler_attributes.h
* around line 200
* should work with recent gcc/clang
*/
#if __has_attribute(__fallthrough__)
# define fallthrough __attribute__((__fallthrough__))
#else
# define fallthrough do {} while (0) /* fallthrough */
#endif
/* 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,246 @@
#include "test-framework/unity.h"
#include "phone_number.h"
#include <stdlib.h>
static char *result = NULL;
void setUp(void)
{
}
void tearDown(void)
{
free(result);
result = NULL;
}
static void test_cleans_the_number(void)
{
const char input[] = "(223) 456-7890";
const char expected[] = "2234567890";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_cleans_numbers_with_dots(void)
{
TEST_IGNORE(); // delete this line to run test
const char input[] = "223.456.7890";
const char expected[] = "2234567890";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_cleans_numbers_with_multiple_spaces(void)
{
TEST_IGNORE();
const char input[] = "223 456 7890 ";
const char expected[] = "2234567890";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_when_9_digits(void)
{
TEST_IGNORE();
const char input[] = "123456789";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_when_11_digits_does_not_start_with_a_1(void)
{
TEST_IGNORE();
const char input[] = "22234567890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_valid_when_11_digits_and_starting_with_1(void)
{
TEST_IGNORE();
const char input[] = "12234567890";
const char expected[] = "2234567890";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void
test_valid_when_11_digits_and_starting_with_1_even_with_punctuation(void)
{
TEST_IGNORE();
const char input[] = "+1 (223) 456-7890";
const char expected[] = "2234567890";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_when_more_than_11_digits(void)
{
TEST_IGNORE();
const char input[] = "321234567890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_with_letters(void)
{
TEST_IGNORE();
const char input[] = "123-abc-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_with_punctuations(void)
{
TEST_IGNORE();
const char input[] = "123-@:!-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_if_area_code_starts_with_0(void)
{
TEST_IGNORE();
const char input[] = "(023) 456-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_if_area_code_starts_with_1(void)
{
TEST_IGNORE();
const char input[] = "(123) 456-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_if_exchange_code_starts_with_0(void)
{
TEST_IGNORE();
const char input[] = "(223) 056-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void test_invalid_if_exchange_code_starts_with_1(void)
{
TEST_IGNORE();
const char input[] = "(223) 156-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void
test_invalid_if_area_code_starts_with_0_on_valid_11_digit_number(void)
{
TEST_IGNORE();
const char input[] = "1 (023) 456-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void
test_invalid_if_area_code_starts_with_1_on_valid_11_digit_number(void)
{
TEST_IGNORE();
const char input[] = "1 (123) 456-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void
test_invalid_if_exchange_code_starts_with_0_on_valid_11_digit_number(void)
{
TEST_IGNORE();
const char input[] = "1 (223) 056-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
static void
test_invalid_if_exchange_code_starts_with_1_on_valid_11_digit_number(void)
{
TEST_IGNORE();
const char input[] = "1 (123) 156-7890";
const char expected[] = "0000000000";
result = phone_number_clean(input);
TEST_ASSERT_EQUAL_STRING(expected, result);
}
int main(void)
{
UnityBegin("test_phone_number.c");
RUN_TEST(test_cleans_the_number);
RUN_TEST(test_cleans_numbers_with_dots);
RUN_TEST(test_cleans_numbers_with_multiple_spaces);
RUN_TEST(test_invalid_when_9_digits);
RUN_TEST(test_invalid_when_11_digits_does_not_start_with_a_1);
RUN_TEST(test_valid_when_11_digits_and_starting_with_1);
RUN_TEST
(test_valid_when_11_digits_and_starting_with_1_even_with_punctuation);
RUN_TEST(test_invalid_when_more_than_11_digits);
RUN_TEST(test_invalid_with_letters);
RUN_TEST(test_invalid_with_punctuations);
RUN_TEST(test_invalid_if_area_code_starts_with_0);
RUN_TEST(test_invalid_if_area_code_starts_with_1);
RUN_TEST(test_invalid_if_exchange_code_starts_with_0);
RUN_TEST(test_invalid_if_exchange_code_starts_with_1);
RUN_TEST(test_invalid_if_area_code_starts_with_0_on_valid_11_digit_number);
RUN_TEST(test_invalid_if_area_code_starts_with_1_on_valid_11_digit_number);
RUN_TEST
(test_invalid_if_exchange_code_starts_with_0_on_valid_11_digit_number);
RUN_TEST
(test_invalid_if_exchange_code_starts_with_1_on_valid_11_digit_number);
return UnityEnd();
}

View File

@@ -4,10 +4,9 @@
#include <stdlib.h>
#endif
resistor_band_t color_code(resistor_band_t *colors)
resistor_band_t color_code(resistor_band_t colors[static 2])
{
resistor_band_t c1=*colors, c2=*(colors+1);
return c1>=BLACK && c1<=WHITE && c2>=BLACK && c2<=WHITE? c1*10+c2: ERROR;
}
@@ -20,7 +19,7 @@ int main(int ac, char **av)
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));
printf("color(%d, %d)=%d\n", *i, *(i+1), color_code(i));
}
}
#endif

View File

@@ -15,7 +15,7 @@ typedef enum {
ERROR=-1,
} resistor_band_t;
extern resistor_band_t color_code(resistor_band_t *);
extern resistor_band_t color_code(resistor_band_t [static 2]);
#ifdef TESTALL
#undef TEST_IGNORE

View File

@@ -0,0 +1,56 @@
# 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):
# 1) copy it into exercise directory
# 2) add ex.h to exercise include file
# 3) add ex.c to exercise source code, and create a suitable main function
# 4) use make with one of the following targets :
# all: compile and run all predefined tests.
# nowarn: compile with no -Werror, and run all predefined tests
# debug: compile with -DDEBUG and run all predefined tests
# mem: perform memcheck with all tests enabled
# unit: build standalone (unit) bimary
# unitnowarn: build standalone (unit) binary with -Werror disabled
# unitdebug: build standalone binary with -DDEBUG
#
# Original 'makefile' targets can be used (test, memcheck, clean, ...)
.PHONY: default all nowarn debug mem unit unitnowarn unitdebug standalone
default: all
ALLSOURCES:=$(wildcard ./*.c)
TESTSOURCES:=$(wildcard ./test_*.c)
SRC:=$(filter-out $(TESTSOURCES),$(ALLSOURCES))
include makefile
all: CFLAGS+=-DTESTALL
all: clean test
nowarn: CFLAGS:=$(filter-out -Werror,$(CFLAGS))
nowarn: clean all
debug: CFLAGS+=-DDEBUG
debug: all
mem: CFLAGS+=-DTESTALL
mem: clean memcheck
unitnowarn: CFLAGS:=$(filter-out -Werror,$(CFLAGS))
unitnowarn: clean unit
unitdebug: CFLAGS+=-DDEBUG
unitdebug: clean unit
unit: CFLAGS+=-DUNIT_TEST
unit: *.c *.h
$(CC) $(CFLAGS) $(SRC) -o tests.out $(LIBS)

View File

@@ -0,0 +1,63 @@
# Help
## Running 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.
```console
$ 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.
[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd
## Submitting your solution
You can submit your solution using the `exercism submit run_length_encoding.c run_length_encoding.h` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [C track's documentation](https://exercism.org/docs/tracks/c)
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
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.
## Submitting Incomplete Solutions
If you are struggling with a particular exercise, it is possible to submit an incomplete solution so you can see how others have completed the exercise.
## Resources
To get help if having trouble, you can use the following resources:
- [StackOverflow][] can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.
- [CPPReference][] can be used to look up information on C concepts, operators, types, standard library functions and more.
- [TutorialsPoint][] has similar content as CPPReference in its C programming section.
- [The C Programming][K&R] book by K&R is the original source of the language and is still useful today.
[c-track]: https://exercism.io/my/tracks/c
[stackoverflow]: http://stackoverflow.com/questions/tagged/c
[cppreference]: https://en.cppreference.com/w/c
[tutorialspoint]: https://www.tutorialspoint.com/cprogramming/
[K&R]: https://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628/

View File

@@ -0,0 +1,47 @@
# Run Length Encoding
Welcome to Run Length Encoding on Exercism's C Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
Implement run-length encoding and decoding.
Run-length encoding (RLE) is a simple form of data compression, where runs
(consecutive data elements) are replaced by just one data value and count.
For example we can represent the original 53 characters with only 13.
```text
"WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB" -> "12WB12W3B24WB"
```
RLE allows the original data to be perfectly reconstructed from
the compressed data, which makes it a lossless data compression.
```text
"AABCCCDEEEE" -> "2AB3CD4E" -> "AABCCCDEEEE"
```
For simplicity, you can assume that the unencoded string will only contain
the letters A through Z (either lower or upper case) and whitespace. This way
data to be encoded will never contain any numbers and numbers inside data to
be decoded always represent the count for the following character.
## Source
### Created by
- @vlzware
### Contributed to by
- @h-3-0
- @patricksjackson
- @QLaille
- @ryanplusplus
- @wolf99
### Based on
Wikipedia - https://en.wikipedia.org/wiki/Run-length_encoding

View File

@@ -0,0 +1,45 @@
#ifndef __BR_COMMON_H
#define __BR_COMMON_H
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 30
*
* __has_attribute is supported on gcc >= 5, clang >= 2.9 and icc >= 17.
* For other compilers, simple implementation (for __falltrough__ only)
*/
#ifndef __has_attribute
# define __has_attribute(x) __GCC4_has_attribute_##x
# define __GCC4_has_attribute___assume_aligned__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___copy__ 0
# define __GCC4_has_attribute___designated_init__ 0
# define __GCC4_has_attribute___externally_visible__ 1
# define __GCC4_has_attribute___no_caller_saved_registers__ 0
# define __GCC4_has_attribute___noclone__ 1
# define __GCC4_has_attribute___nonstring__ 0
# define __GCC4_has_attribute___no_sanitize_address__ (__GNUC_MINOR__ >= 8)
# define __GCC4_has_attribute___no_sanitize_undefined__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___fallthrough__ 0
# define __GCC4_has_attribute___fallthrough__ 0
#endif
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 200
*/
#if __has_attribute(__fallthrough__)
# define fallthrough __attribute__((__fallthrough__))
#else
# define fallthrough do {} while (0); /* fallthrough */
#endif
/* 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 /* __BR_COMMON_H */

View File

@@ -0,0 +1,26 @@
#include "run_length_encoding.h"
/* 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, what;
char *res;
what = *av[arg++]; /* 'e', 'd' */
for (; arg<ac; ++arg) {
switch (what) {
case 'e':
printf("enc[%s]=%d [%s]\n", av[arg], encode_len(av[arg]),
res=encode(av[arg]));
printf("dec[%s]=%d [%s]\n", res, decode_len(res), decode(res));
break;
case 'd':
printf("dec[%s]=%d [%s]\n", av[arg], encode_len(av[arg]),
res=decode(av[arg]));
printf("enc[%s]=%d [%s]\n", res, decode_len(res), decode(res));
}
}
}
#endif

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: ./*.c ./*.h
@echo Compiling $@
@$(CC) $(ASANFLAGS) $(CFLAGS) test-framework/unity.c ./*.c -o memcheck.out $(LIBS)
@./memcheck.out
@echo "Memory check passed"
.PHONY: clean
clean:
rm -rf *.o *.out *.out.dSYM
tests.out: ./*.c ./*.h
@echo Compiling $@
@$(CC) $(CFLAGS) test-framework/unity.c ./*.c -o tests.out $(LIBS)

View File

@@ -0,0 +1,101 @@
#include "run_length_encoding.h"
#include <stdio.h>
#include <malloc.h>
#include <string.h>
#include <ctype.h>
#include <stdlib.h>
/* We have 2 choices for encoding : allocate a max length string (i.e. the
* size of input string), maybe shrinking with realloc() before returning or
* trying to calculate first the the exact output length.
*
* For decoding, if we want to avoid an arbitrary MAX_LENGTH, or, worse,
* a smaller string with multiple realloc() when we need more space, we
* also can first calculate the output string size.
*
* We will go for the second method (calculate exact length first for both
* encode() and decode().
*
* Additional checks: we allow only printable characters (isprint()), and
* disallow digits for encode() and ending digits for decode().
*/
int encode_len(const char *str)
{
int len=0, c;
if (!str)
return -1;
for (c=*str; c; c=*str) {
int sublen=0, log=1;
if (isdigit(c) || !isprint(c))
return -1;
for (; *str == c; str++, sublen++)
;
if (sublen > 1)
do
log++;
while (sublen /= 10);
len += log;
}
return len+1;
}
char *encode(const char *str)
{
char *res, *p;
int c, len;
if (!(str && (len = encode_len(str)) > 0 && (res = malloc(len))))
return NULL;
p=res;
for (c=*str; c; *p++ = c, c=*str) {
int sublen = 0;
for (; *str == c; str++, sublen++) /* count consecutive chars */
;
if (sublen > 1)
p += sprintf(p, "%d", sublen);
}
*p = 0;
return res;
}
int decode_len(const char *str)
{
int len=0;
if (!str)
return -1;
for (int c = *str; c; c = *++str, len++) {
if (isdigit(c)) {
int sublen = strtold(str, (char **)&str);
if ((c = *str) == 0 || !isprint(c)) /* the char to duplicate */
return -1;
len += sublen - 1;
}
}
return len+1;
}
char *decode(const char *str)
{
char *res, *p;
int len;
if (!(str && (len = decode_len(str)) > 0 && (res = malloc(len))))
return NULL;
p=res;
for (int c = *str; c; *p++ = c, c = *++str) {
if (isdigit(c)) {
int sublen = strtold(str, (char **)&str);
c = *str; /* the char to duplicate */
while (--sublen) /* or (optimized) memset ? */
*p++ = c;
}
}
*p = 0;
return res;
}

View File

@@ -0,0 +1,13 @@
#ifndef RUN_LENGTH_ENCODING_H
#define RUN_LENGTH_ENCODING_H
int encode_len(const char *str);
char *encode(const char *text);
int decode_len(const char *str);
char *decode(const char *data);
#endif
#include "br-common.h"

View File

@@ -0,0 +1,156 @@
#include "test-framework/unity.h"
#include "run_length_encoding.h"
#include <stdlib.h>
void setUp(void)
{
}
void tearDown(void)
{
}
static void test_encode_empty_string(void)
{
char *res = encode("");
TEST_ASSERT_EQUAL_STRING("", res);
free(res);
}
static void test_encode_single_characters_only_are_encoded_without_count(void)
{
TEST_IGNORE(); // delete this line to run test
char *res = encode("XYZ");
TEST_ASSERT_EQUAL_STRING("XYZ", res);
free(res);
}
static void test_encode_string_with_no_single_characters(void)
{
TEST_IGNORE();
char *res = encode("AABBBCCCC");
TEST_ASSERT_EQUAL_STRING("2A3B4C", res);
free(res);
}
static void test_encode_single_characters_mixed_with_repeated_characters(void)
{
TEST_IGNORE();
char *res = encode("WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB");
TEST_ASSERT_EQUAL_STRING("12WB12W3B24WB", res);
free(res);
}
static void test_encode_multiple_whitespace_mixed_in_string(void)
{
TEST_IGNORE();
char *res = encode(" hsqq qww ");
TEST_ASSERT_EQUAL_STRING("2 hs2q q2w2 ", res);
free(res);
}
static void test_encode_lowercase_characters(void)
{
TEST_IGNORE();
char *res = encode("aabbbcccc");
TEST_ASSERT_EQUAL_STRING("2a3b4c", res);
free(res);
}
static void test_decode_empty_string(void)
{
TEST_IGNORE();
char *res = decode("");
TEST_ASSERT_EQUAL_STRING("", res);
free(res);
}
static void test_decode_single_characters_only(void)
{
TEST_IGNORE();
char *res = decode("XYZ");
TEST_ASSERT_EQUAL_STRING("XYZ", res);
free(res);
}
static void test_decode_string_with_no_single_characters(void)
{
TEST_IGNORE();
char *res = decode("2A3B4C");
TEST_ASSERT_EQUAL_STRING("AABBBCCCC", res);
free(res);
}
static void test_decode_single_characters_with_repeated_characters(void)
{
TEST_IGNORE();
char *res = decode("12WB12W3B24WB");
TEST_ASSERT_EQUAL_STRING
("WWWWWWWWWWWWBWWWWWWWWWWWWBBBWWWWWWWWWWWWWWWWWWWWWWWWB", res);
free(res);
}
static void test_decode_multiple_whitespace_mixed_in_string(void)
{
TEST_IGNORE();
char *res = decode("2 hs2q q2w2 ");
TEST_ASSERT_EQUAL_STRING(" hsqq qww ", res);
free(res);
}
static void test_decode_lower_case_string(void)
{
TEST_IGNORE();
char *res = decode("2a3b4c");
TEST_ASSERT_EQUAL_STRING("aabbbcccc", res);
free(res);
}
static void
test_consistency_encode_followed_by_decode_gives_original_string(void)
{
TEST_IGNORE();
char *res_enc = encode("zzz ZZ zZ");
char *res_dec = decode(res_enc);
TEST_ASSERT_EQUAL_STRING("zzz ZZ zZ", res_dec);
free(res_enc);
free(res_dec);
}
static void test_encode_invalid_input_contains_digits(void)
{
TEST_IGNORE();
char *res_enc = encode("AABB1A");
TEST_ASSERT_NULL(res_enc);
}
static void test_decode_invalid_input_ends_with_digits(void)
{
TEST_IGNORE();
char *res_dec = decode("AABBA2");
TEST_ASSERT_NULL(res_dec);
}
int main(void)
{
UnityBegin("test_run_length_encoding.c");
RUN_TEST(test_encode_empty_string);
RUN_TEST(test_encode_single_characters_only_are_encoded_without_count);
RUN_TEST(test_encode_string_with_no_single_characters);
RUN_TEST(test_encode_single_characters_mixed_with_repeated_characters);
RUN_TEST(test_encode_multiple_whitespace_mixed_in_string);
RUN_TEST(test_encode_lowercase_characters);
RUN_TEST(test_decode_empty_string);
RUN_TEST(test_decode_single_characters_only);
RUN_TEST(test_decode_string_with_no_single_characters);
RUN_TEST(test_decode_single_characters_with_repeated_characters);
RUN_TEST(test_decode_multiple_whitespace_mixed_in_string);
RUN_TEST(test_decode_lower_case_string);
RUN_TEST(test_consistency_encode_followed_by_decode_gives_original_string);
RUN_TEST(test_encode_invalid_input_contains_digits);
RUN_TEST(test_decode_invalid_input_ends_with_digits);
return UnityEnd();
}

44
c/setup-exercise.sh Executable file
View File

@@ -0,0 +1,44 @@
#!/bin/bash
CMD=${0##*/}
ROOT=${0%/*}
usage() {
printf "usage: %s destination_dir\n" "$CMD"
}
if (($# == 0)); then
"$0" "$PWD"
exit 0
fi
if (($# != 1)) || [[ ! -d $1 ]]; then
usage
exit 1
fi
TEMPLATES="$ROOT/templates"
# copy main.c, GNUmakefile and br-common.h
echo -n "copying GNUmakefile and br-common.h files... "
cp -p "$TEMPLATES/"{GNUmakefile,br-common.h,main.c} "$1"
echo done.
# add include br-common.h in exercise include file
INC=${1%/}
INC=${INC##*/}
INC="$1/${INC//-/_}.h"
if [[ ! -f "$INC" ]]; then
printf "cannot find <%s> include file\n" "$INC"
exit 1
fi
STR='#include "br-common.h"'
# likely very weak
# TODO: check if not already inserted
#echo -n "editing $INC include file... "
#sed -i "/#define /a $STR" "$INC"
#echo done.
echo -n "editing $INC include file... "
echo "" >> "$INC"
echo "$STR" >> "$INC"
echo done.

View File

@@ -27,6 +27,10 @@
default: all
ALLSOURCES:=$(wildcard ./*.c)
TESTSOURCES:=$(wildcard ./test_*.c)
SRC:=$(filter-out $(TESTSOURCES),$(ALLSOURCES))
include makefile
all: CFLAGS+=-DTESTALL
@@ -48,5 +52,5 @@ unitdebug: CFLAGS+=-DDEBUG
unitdebug: clean unit
unit: CFLAGS+=-DUNIT_TEST
unit: src/*.c src/*.h
$(CC) $(CFLAGS) src/*.c -o tests.out $(LIBS)
unit: *.c *.h
$(CC) $(CFLAGS) $(SRC) -o tests.out $(LIBS)

45
c/templates/br-common.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef __BR_COMMON_H
#define __BR_COMMON_H
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 30
*
* __has_attribute is supported on gcc >= 5, clang >= 2.9 and icc >= 17.
* For other compilers, simple implementation (for __falltrough__ only)
*/
#ifndef __has_attribute
# define __has_attribute(x) __GCC4_has_attribute_##x
# define __GCC4_has_attribute___assume_aligned__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___copy__ 0
# define __GCC4_has_attribute___designated_init__ 0
# define __GCC4_has_attribute___externally_visible__ 1
# define __GCC4_has_attribute___no_caller_saved_registers__ 0
# define __GCC4_has_attribute___noclone__ 1
# define __GCC4_has_attribute___nonstring__ 0
# define __GCC4_has_attribute___no_sanitize_address__ (__GNUC_MINOR__ >= 8)
# define __GCC4_has_attribute___no_sanitize_undefined__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___fallthrough__ 0
# define __GCC4_has_attribute___fallthrough__ 0
#endif
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 200
*/
#if __has_attribute(__fallthrough__)
# define fallthrough __attribute__((__fallthrough__))
#else
# define fallthrough do {} while (0); /* fallthrough */
#endif
/* 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 /* __BR_COMMON_H */

View File

@@ -1,16 +0,0 @@
/* 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

@@ -2,11 +2,11 @@
* https://github.com/braoult/exercism/blob/master/c/templates/GNUmakefile
*/
#if defined UNIT_TEST || defined DEBUG
#include <stdio.h>
#include <stdlib.h>
# include <stdio.h>
# include <stdlib.h>
#endif
#ifdef TESTALL
#undef TEST_IGNORE
#define TEST_IGNORE() {}
# undef TEST_IGNORE
# define TEST_IGNORE() {}
#endif

56
c/two-bucket/GNUmakefile Normal file
View File

@@ -0,0 +1,56 @@
# 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):
# 1) copy it into exercise directory
# 2) add ex.h to exercise include file
# 3) add ex.c to exercise source code, and create a suitable main function
# 4) use make with one of the following targets :
# all: compile and run all predefined tests.
# nowarn: compile with no -Werror, and run all predefined tests
# debug: compile with -DDEBUG and run all predefined tests
# mem: perform memcheck with all tests enabled
# unit: build standalone (unit) bimary
# unitnowarn: build standalone (unit) binary with -Werror disabled
# unitdebug: build standalone binary with -DDEBUG
#
# Original 'makefile' targets can be used (test, memcheck, clean, ...)
.PHONY: default all nowarn debug mem unit unitnowarn unitdebug standalone
default: all
ALLSOURCES:=$(wildcard ./*.c)
TESTSOURCES:=$(wildcard ./test_*.c)
SRC:=$(filter-out $(TESTSOURCES),$(ALLSOURCES))
include makefile
all: CFLAGS+=-DTESTALL
all: clean test
nowarn: CFLAGS:=$(filter-out -Werror,$(CFLAGS))
nowarn: clean all
debug: CFLAGS+=-DDEBUG
debug: all
mem: CFLAGS+=-DTESTALL
mem: clean memcheck
unitnowarn: CFLAGS:=$(filter-out -Werror,$(CFLAGS))
unitnowarn: clean unit
unitdebug: CFLAGS+=-DDEBUG
unitdebug: clean unit
unit: CFLAGS+=-DUNIT_TEST
unit: *.c *.h
$(CC) $(CFLAGS) $(SRC) -o tests.out $(LIBS)

63
c/two-bucket/HELP.md Normal file
View File

@@ -0,0 +1,63 @@
# Help
## Running 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.
```console
$ 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.
[3-tdd-rules]: http://butunclebob.com/ArticleS.UncleBob.TheThreeRulesOfTdd
## Submitting your solution
You can submit your solution using the `exercism submit two_bucket.c two_bucket.h` command.
This command will upload your solution to the Exercism website and print the solution page's URL.
It's possible to submit an incomplete solution which allows you to:
- See how others have completed the exercise
- Request help from a mentor
## Need to get help?
If you'd like help solving the exercise, check the following pages:
- The [C track's documentation](https://exercism.org/docs/tracks/c)
- [Exercism's support channel on gitter](https://gitter.im/exercism/support)
- The [Frequently Asked Questions](https://exercism.org/docs/using/faqs)
Should those resources not suffice, you could submit your (incomplete) solution to request mentoring.
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.
## Submitting Incomplete Solutions
If you are struggling with a particular exercise, it is possible to submit an incomplete solution so you can see how others have completed the exercise.
## Resources
To get help if having trouble, you can use the following resources:
- [StackOverflow][] can be used to search for your problem and see if it has been answered already. You can also ask and answer questions.
- [CPPReference][] can be used to look up information on C concepts, operators, types, standard library functions and more.
- [TutorialsPoint][] has similar content as CPPReference in its C programming section.
- [The C Programming][K&R] book by K&R is the original source of the language and is still useful today.
[c-track]: https://exercism.io/my/tracks/c
[stackoverflow]: http://stackoverflow.com/questions/tagged/c
[cppreference]: https://en.cppreference.com/w/c
[tutorialspoint]: https://www.tutorialspoint.com/cprogramming/
[K&R]: https://www.amazon.com/Programming-Language-2nd-Brian-Kernighan/dp/0131103628/

49
c/two-bucket/README.md Normal file
View File

@@ -0,0 +1,49 @@
# Two Bucket
Welcome to Two Bucket on Exercism's C Track.
If you need help running the tests or submitting your code, check out `HELP.md`.
## Instructions
Given two buckets of different size, demonstrate how to measure an exact number of liters by strategically transferring liters of fluid between the buckets.
Since this mathematical problem is fairly subject to interpretation / individual approach, the tests have been written specifically to expect one overarching solution.
To help, the tests provide you with which bucket to fill first. That means, when starting with the larger bucket full, you are NOT allowed at any point to have the smaller bucket full and the larger bucket empty (aka, the opposite starting point); that would defeat the purpose of comparing both approaches!
Your program will take as input:
- the size of bucket one
- the size of bucket two
- the desired number of liters to reach
- which bucket to fill first, either bucket one or bucket two
Your program should determine:
- the total number of "moves" it should take to reach the desired number of liters, including the first fill
- which bucket should end up with the desired number of liters (let's say this is bucket A) - either bucket one or bucket two
- how many liters are left in the other bucket (bucket B)
Note: any time a change is made to either or both buckets counts as one (1) move.
Example:
Bucket one can hold up to 7 liters, and bucket two can hold up to 11 liters. Let's say bucket one, at a given step, is holding 7 liters, and bucket two is holding 8 liters (7,8). If you empty bucket one and make no change to bucket two, leaving you with 0 liters and 8 liters respectively (0,8), that counts as one "move". Instead, if you had poured from bucket one into bucket two until bucket two was full, leaving you with 4 liters in bucket one and 11 liters in bucket two (4,11), that would count as only one "move" as well.
To conclude, the only valid moves are:
- pouring from either bucket to another
- emptying either bucket and doing nothing to the other
- filling either bucket and doing nothing to the other
Written with <3 at [Fullstack Academy](http://www.fullstackacademy.com/) by Lindsay Levine.
## Source
### Created by
- @ryanplusplus
### Contributed to by
- @wolf99
### Based on
Water Pouring Problem - http://demonstrations.wolfram.com/WaterPouringProblem/

45
c/two-bucket/br-common.h Normal file
View File

@@ -0,0 +1,45 @@
#ifndef __BR_COMMON_H
#define __BR_COMMON_H
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 30
*
* __has_attribute is supported on gcc >= 5, clang >= 2.9 and icc >= 17.
* For other compilers, simple implementation (for __falltrough__ only)
*/
#ifndef __has_attribute
# define __has_attribute(x) __GCC4_has_attribute_##x
# define __GCC4_has_attribute___assume_aligned__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___copy__ 0
# define __GCC4_has_attribute___designated_init__ 0
# define __GCC4_has_attribute___externally_visible__ 1
# define __GCC4_has_attribute___no_caller_saved_registers__ 0
# define __GCC4_has_attribute___noclone__ 1
# define __GCC4_has_attribute___nonstring__ 0
# define __GCC4_has_attribute___no_sanitize_address__ (__GNUC_MINOR__ >= 8)
# define __GCC4_has_attribute___no_sanitize_undefined__ (__GNUC_MINOR__ >= 9)
# define __GCC4_has_attribute___fallthrough__ 0
# define __GCC4_has_attribute___fallthrough__ 0
#endif
/* ${LINUX_SRC}/include/linux/compiler_attributes.h, around line 200
*/
#if __has_attribute(__fallthrough__)
# define fallthrough __attribute__((__fallthrough__))
#else
# define fallthrough do {} while (0); /* fallthrough */
#endif
/* 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 /* __BR_COMMON_H */

26
c/two-bucket/main.c Normal file
View File

@@ -0,0 +1,26 @@
/* Standalone tests.
* See GNUmakefile below for explanation
* https://github.com/braoult/exercism/blob/master/c/templates/GNUmakefile
*/
#include "two_bucket.h"
#ifdef UNIT_TEST
int main(int ac, char **av)
{
int arg=1;
int b1, b2, goal, start;
bucket_result_t res;;
for (; arg<ac-3; arg+=4) {
b1 = atoi(av[arg]);
b2 = atoi(av[arg+1]);
goal = atoi(av[arg+2]);
start = atoi(av[arg+3]);
printf("b1=%d, b2=%d, goal=%d, start=%d\n", b1, b2, goal, start);
res = measure(b1, b2, goal, start);
printf(" pos=%d count=%d goal=%d liters=%d\n",
res.possible, res.move_count, res.goal_bucket,
res.other_bucket_liters);
}
}
#endif

37
c/two-bucket/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: ./*.c ./*.h
@echo Compiling $@
@$(CC) $(ASANFLAGS) $(CFLAGS) test-framework/unity.c ./*.c -o memcheck.out $(LIBS)
@./memcheck.out
@echo "Memory check passed"
.PHONY: clean
clean:
rm -rf *.o *.out *.out.dSYM
tests.out: ./*.c ./*.h
@echo Compiling $@
@$(CC) $(CFLAGS) test-framework/unity.c ./*.c -o tests.out $(LIBS)

View File

@@ -0,0 +1,139 @@
#include "test-framework/unity.h"
#include "two_bucket.h"
void setUp(void)
{
}
void tearDown(void)
{
}
static void assert_results_match(bucket_result_t expected,
bucket_result_t actual)
{
TEST_ASSERT_EQUAL(expected.possible, actual.possible);
if (expected.possible) {
TEST_ASSERT_EQUAL(expected.move_count, actual.move_count);
TEST_ASSERT_EQUAL(expected.goal_bucket, actual.goal_bucket);
TEST_ASSERT_EQUAL(expected.other_bucket_liters,
actual.other_bucket_liters);
}
}
static void
test_measure_using_bucket_one_of_size_3_and_bucket_two_of_size_5_start_with_bucket_one
(void) {
bucket_result_t expected = {.possible = true,.move_count = 4,.goal_bucket =
BUCKET_ID_1,.other_bucket_liters = 5
};
bucket_result_t actual = measure(3, 5, 1, BUCKET_ID_1);
assert_results_match(expected, actual);
}
static void
test_measure_using_bucket_one_of_size_3_and_bucket_two_of_size_5_start_with_bucket_two
(void) {
TEST_IGNORE();
bucket_result_t expected = {.possible = true,.move_count = 8,.goal_bucket =
BUCKET_ID_2,.other_bucket_liters = 3
};
bucket_result_t actual = measure(3, 5, 1, BUCKET_ID_2);
assert_results_match(expected, actual);
}
static void
test_measure_using_bucket_one_of_size_7_and_bucket_two_of_size_11_start_with_bucket_one
(void) {
TEST_IGNORE();
bucket_result_t expected = {.possible = true,.move_count = 14,.goal_bucket =
BUCKET_ID_1,.other_bucket_liters = 11
};
bucket_result_t actual = measure(7, 11, 2, BUCKET_ID_1);
assert_results_match(expected, actual);
}
static void
test_measure_using_bucket_one_of_size_7_and_bucket_two_of_size_11_start_with_bucket_two
(void) {
TEST_IGNORE();
bucket_result_t expected = {.possible = true,.move_count = 18,.goal_bucket =
BUCKET_ID_2,.other_bucket_liters = 7
};
bucket_result_t actual = measure(7, 11, 2, BUCKET_ID_2);
assert_results_match(expected, actual);
}
static void
test_measure_one_step_using_bucket_one_of_size_1_and_bucket_two_of_size_3_start_with_bucket_two
(void) {
TEST_IGNORE();
bucket_result_t expected = {.possible = true,.move_count = 1,.goal_bucket =
BUCKET_ID_2,.other_bucket_liters = 0
};
bucket_result_t actual = measure(1, 3, 3, BUCKET_ID_2);
assert_results_match(expected, actual);
}
static void
test_measure_using_bucket_one_of_size_2_and_bucket_two_of_size_3_start_with_bucket_one_and_end_with_bucket_two
(void) {
TEST_IGNORE();
bucket_result_t expected = {.possible = true,.move_count = 2,.goal_bucket =
BUCKET_ID_2,.other_bucket_liters = 2
};
bucket_result_t actual = measure(2, 3, 3, BUCKET_ID_1);
assert_results_match(expected, actual);
}
static void test_not_possible_to_reach_the_goal(void)
{
TEST_IGNORE();
bucket_result_t expected = {.possible = false };
bucket_result_t actual = measure(6, 15, 5, BUCKET_ID_1);
assert_results_match(expected, actual);
}
static void
test_with_the_same_buckets_but_a_different_goal_then_it_is_possible(void)
{
TEST_IGNORE();
bucket_result_t expected = {.possible = true,.move_count = 10,.goal_bucket =
BUCKET_ID_2,.other_bucket_liters = 0
};
bucket_result_t actual = measure(6, 15, 9, BUCKET_ID_1);
assert_results_match(expected, actual);
}
static void test_goal_larger_than_both_buckets_is_impossible(void)
{
TEST_IGNORE();
bucket_result_t expected = {.possible = false };
bucket_result_t actual = measure(5, 7, 8, BUCKET_ID_1);
assert_results_match(expected, actual);
}
int main(void)
{
UnityBegin("test_two_bucket.c");
RUN_TEST
(test_measure_using_bucket_one_of_size_3_and_bucket_two_of_size_5_start_with_bucket_one);
RUN_TEST
(test_measure_using_bucket_one_of_size_3_and_bucket_two_of_size_5_start_with_bucket_two);
RUN_TEST
(test_measure_using_bucket_one_of_size_7_and_bucket_two_of_size_11_start_with_bucket_one);
RUN_TEST
(test_measure_using_bucket_one_of_size_7_and_bucket_two_of_size_11_start_with_bucket_two);
RUN_TEST
(test_measure_one_step_using_bucket_one_of_size_1_and_bucket_two_of_size_3_start_with_bucket_two);
RUN_TEST
(test_measure_using_bucket_one_of_size_2_and_bucket_two_of_size_3_start_with_bucket_one_and_end_with_bucket_two);
RUN_TEST(test_not_possible_to_reach_the_goal);
RUN_TEST
(test_with_the_same_buckets_but_a_different_goal_then_it_is_possible);
RUN_TEST(test_goal_larger_than_both_buckets_is_impossible);
return UnityEnd();
}

228
c/two-bucket/two_bucket.c Normal file
View File

@@ -0,0 +1,228 @@
#include "two_bucket.h"
#include <stdlib.h>
/* We will choose to use a board of (X,Y) squares, with the sizes of the 2
* buckets (in fact +1, as buckets can have values 0...max), where we move
* the current point at each step.
* Current square (x,y) is enough to describe the status of the
* 2 buckets: b1 has x liters, b2 has y liters.
* At square (x, y), a maximum of 6 moves are possible :
* - (0, y) = empty b1
* - (X, y) = fill b1
* - (x, 0) = empty b2
* - (x, Y) = fill b2
* - (x - d, y + d) = b1 -> b2, with d = min(Y-y, x)
* - (x + d, y - d) = b2 -> b1, with d = min(X-x, y)
* The board keeps tracks of visited squares: We cannot pass twice the
* same one.
* We will use BFS algorithm from initial square, which is (0, Y) or (X, 0).
* We stop when x or y are equal to goal, or when no moves are possible.
*
*/
/* The board contains a positive integer for its current move, otherwise the
* following values
*/
#define AVAILABLE 0
#define FORBIDDEN (-1)
#define GOAL (-2)
/* from (col, row) to real array position (square), and vice-et-versa.
* X is column size.
*/
#define SQUARE(col, row, board) ((row) * (board->X) + (col))
#define COL(square, board) ((square) % (board->X))
#define ROW(square, board) ((square) / (board->X))
/* danger zone !! - this macro is BAD™ in general, dangerous double
* evaluation, but ok here.
* If unsure why this is very bad, leave me a comment on exercism.
*/
#define MIN(x, y) ((x) <= (y)? (x): (y))
/* a queue for moves (BFS). I use a max X*Y array, a linked list would
* be much better, would the buckets sizes being extremely huge.
*/
struct stack {
int first;
int last;
int s[];
};
/* our game board
*/
struct board {
int X;
int Y;
int board[];
};
static inline void board_init(struct board *board, int X, int Y, int goal)
{
int i;
board->X=X;
board->Y=Y;
for (i=0; i < board->X*board->Y; ++i)
*(board->board+i) = AVAILABLE;
/* set target on goal row */
if (board->X > goal) {
for (i = 0; i < board->Y; ++i)
board->board[SQUARE(goal, i, board)] = GOAL;
}
/* set target on goal col */
if (board->Y > goal) {
for (i = 0; i < board->X; ++i)
board->board[SQUARE(i, goal, board)] = GOAL;
}
}
static inline void enqueue(struct stack *s, int v)
{
s->s[s->last++] = v;
}
static inline int dequeue(struct stack *s)
{
return s->first < s->last ? s->s[s->first++] : -1;
}
static int move(struct board *board, struct stack *stack, int col, int row,
int level)
{
board->board[SQUARE(col, row, board)] = level;
enqueue (stack, SQUARE(col, row, board));
return 0;
}
static int move_maybe(struct board *board, struct stack *stack,
int col, int row, int level)
{
switch (board->board[SQUARE(col, row, board)]) {
case GOAL:
return 1;
case AVAILABLE:
return move(board, stack, col, row, level);
default:
return 0;
}
}
static bucket_result_t board_bfs(struct board *board, struct stack *stack,
const int goal)
{
int level = 0;
int cur_square;
int min, row, col, col1, row1;
int X=board->X, Y=board->Y;
bucket_result_t res = { .possible = false, .move_count = 0 };
/* now we consume as we can... */
while ((cur_square = dequeue(stack)) >= 0) {
col=COL(cur_square, board);
row=ROW(cur_square, board);
level=board->board[cur_square];
if (col > 0) {
col1 = 0;
row1 = row;
if (move_maybe(board, stack, col1, row1, level+1))
goto found;
}
if (row > 0) {
col1 = col;
row1 = 0;
if (move_maybe(board, stack, col1, row1, level+1))
goto found;
}
if (col < X-1) {
col1 = X-1;
row1 = row;
if (move_maybe(board, stack, col1, row1, level+1))
goto found;
}
if (row < Y-1) {
col1 = col;
row1 = Y-1;
if (move_maybe(board, stack, col1, row1, level+1))
goto found;
}
if (col > 0 && row < Y-1) {
min = MIN(col, Y-row-1);
col1 = col - min;
row1 = row + min;
if (move_maybe(board, stack, col1, row1, level+1))
goto found;
}
if (row > 0 && col < X-1) {
min = MIN(row, X-col-1);
col1 = col + min;
row1 = row - min;
if (move_maybe(board, stack, col1, row1, level+1))
goto found;
}
}
goto end; /* fail: no more moves */
found:
res.possible = true;
res.move_count = level+1;
if (col1 == goal) {
res.goal_bucket=BUCKET_ID_1;
res.other_bucket_liters=row1;
} else {
res.goal_bucket=BUCKET_ID_2;
res.other_bucket_liters=col1;
}
end:
return res;
}
bucket_result_t measure(const bucket_liters_t b1,
const bucket_liters_t b2,
const bucket_liters_t goal,
const bucket_id_t start)
{
bucket_result_t res = {
.possible = false,
.move_count = 0,
.other_bucket_liters = 0
};
struct board *board;
struct stack *stack;
/* impossible goal */
if (goal > b1 && goal > b2)
return res;
/* initial move is the solution ? */
if ((start == BUCKET_ID_1 && goal == b1) ||
(start == BUCKET_ID_2 && goal == b2)) {
res.possible = true;
res.move_count = 1;
res.goal_bucket = start;
return res;
}
if (!(stack = malloc(sizeof(*stack) + sizeof(*stack->s)*(b1+1)*(b2+1))))
return res;
//int i;
stack->first = stack->last = 0;
if (!(board = malloc(sizeof(*board) + sizeof(*board->board)*(b1+1)*(b2+1)))) {
free(stack);
return res;
}
board_init(board, b1+1, b2+1, goal);
/* only for exercism. better solutions could be found. Remove the next
* 3 lines to allow a different starting bucket (possibly better solution).
*/
board->board[SQUARE(0, 0, board)] = FORBIDDEN;
board->board[SQUARE(0, b2, board)] = FORBIDDEN;
board->board[SQUARE(b1, 0, board)] = FORBIDDEN;
/* initial move */
move(board, stack, start==BUCKET_ID_1? b1: 0, start==BUCKET_ID_2? b2: 0, 1);
res = board_bfs(board, stack, goal);
free(stack);
free(board);
return res;
}

27
c/two-bucket/two_bucket.h Normal file
View File

@@ -0,0 +1,27 @@
#ifndef TWO_BUCKET_H
#define TWO_BUCKET_H
#include <stdbool.h>
typedef enum {
BUCKET_ID_1,
BUCKET_ID_2
} bucket_id_t;
typedef unsigned int bucket_liters_t;
typedef struct {
bool possible;
int move_count;
bucket_id_t goal_bucket;
bucket_liters_t other_bucket_liters;
} bucket_result_t;
bucket_result_t measure(bucket_liters_t bucket_1_size,
bucket_liters_t bucket_2_size,
bucket_liters_t goal_volume,
bucket_id_t start_bucket);
#endif /* TWO_BUCKETS_H */
#include "br-common.h"

View File

@@ -18,10 +18,10 @@
* The following will return INVALID_WORD:
* P2P
* 0xFF
* A'2
* A''B
* The following will return 2 numbers/words:
* 1'2
* A''B
* A'2
*/
/* get next word in string