diff --git a/c/phone-number/HELP.md b/c/phone-number/HELP.md new file mode 100644 index 0000000..3274961 --- /dev/null +++ b/c/phone-number/HELP.md @@ -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/ \ No newline at end of file diff --git a/c/phone-number/br-common.h b/c/phone-number/br-common.h new file mode 100644 index 0000000..ca7968e --- /dev/null +++ b/c/phone-number/br-common.h @@ -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 +# include +#endif + +#ifdef TESTALL +# undef TEST_IGNORE +# define TEST_IGNORE() {} +#endif + +#endif /* __BR_COMMON_H */ diff --git a/c/phone-number/phone_number.c b/c/phone-number/phone_number.c new file mode 100644 index 0000000..e71f357 --- /dev/null +++ b/c/phone-number/phone_number.c @@ -0,0 +1,80 @@ +#include +#include +#include +#include + +#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. + */ +char *phone_number_clean(const char *input) +{ + char *scan="%m[+(0-9]%*[()-. ]%m[0-9]%*[()-. ]%m[0-9]%*[-. ]%m[0-9]"; + char *sn[4]; + uint64_t num[4]; + uint64_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 10000000000) /* 1 000 000 0000 */ + *p -= 10000000000; + if (*p > 9999999999 || /* 999 999 9999 */ + *p < 2000000000) /* 200 000 0000 */ + return res; + break; + case 4: /* area */ + if (*p != 1) + return res; + p++; + fallthrough; /* only gcc>=7 & clang>=12 */ + case 3: /* last 3 numbers */ + if (*p < 200 || *p > 999 || + *(p+1) < 200 || *(p+1) > 999 || + *(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 [%s]\n", res); + free(res); + } +} +#endif diff --git a/c/phone-number/phone_number.h b/c/phone-number/phone_number.h new file mode 100644 index 0000000..a24b3b6 --- /dev/null +++ b/c/phone-number/phone_number.h @@ -0,0 +1,10 @@ +#ifndef PHONE_NUMBER_H +#define PHONE_NUMBER_H + +#include "br-common.h" + +#define NUMBER_LENGTH 10 + +char *phone_number_clean(const char *input); + +#endif diff --git a/c/phone-number/test_phone_number.c b/c/phone-number/test_phone_number.c new file mode 100644 index 0000000..a15b1da --- /dev/null +++ b/c/phone-number/test_phone_number.c @@ -0,0 +1,246 @@ +#include "test-framework/unity.h" +#include "phone_number.h" +#include + +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(); +} diff --git a/c/templates/br-common.h b/c/templates/br-common.h new file mode 100644 index 0000000..ca7968e --- /dev/null +++ b/c/templates/br-common.h @@ -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 +# include +#endif + +#ifdef TESTALL +# undef TEST_IGNORE +# define TEST_IGNORE() {} +#endif + +#endif /* __BR_COMMON_H */