#include #include #include #include // Not used in the challenge, only for assertions. #include // 1st challenge /** * @brief Function to calculate the length of a string * * @details This function calculates the length of a string by counting the * number of characters until the null terminator. * * @param `string` The string to calculate the length of * * @return The length of the string */ int my_strlen(const char *string) { int i = 0; int total = 0; char letter = string[0]; while (letter != '\0') { total++; letter = string[++i]; } return total; }; /** * @brief Function to assert the length of a string * * @details This function asserts that the length of a string is equal to the * length of the string calculated by the standard strlen function. * * @param `string` The string to assert the length of * * @return 0 if the assertion passes */ int assert_string(const char *string) { assert(my_strlen(string) == strlen(string)); return 0; } // 1st challenge, application void strlen_test(void) { const char *test1 = "IAE CLUB"; const char *test2 = "House Of Code"; const char *test3 = "G"; const char *test4 = ""; printf(" Test 1: %s\n", test1); printf(" Length : %d\n", my_strlen(test1)); assert_string(test1); printf(" Test 2: %s\n", test2); printf(" Length : %d\n", my_strlen(test2)); assert_string(test2); printf(" Test 3: %s\n", test3); printf(" Length : %d\n", my_strlen(test3)); assert_string(test3); printf(" Test 4: %s\n", test4); printf(" Length : %d\n", my_strlen(test4)); assert_string(test4); } // 2nd challenge /** * @brief Function to reverse a string * * @details This function reverses a string by swapping the first and last * characters, then moving towards the center of the string. * -------------------------------------------------------------- * theoretical visualisation: * [(A)][B][C][D][(E)] -> [E][(B)][C][D][(A)] -> [E][D][C][B][A] -> Done * * @param `str` The string to reverse */ void reverse_string(char *str) { int first = 0, last = my_strlen(str) - 1; // we remove the \0 // WARN: don't put this stupid comment while (first < last) { char temporary = str[first]; str[first] = str[last]; str[last] = temporary; first++, last--; } } /** * @brief Function to assert the reverse of a string * * @details This function asserts that the reverse of a string is equal to the * original string. * * @param `string` The string to assert the reverse of * * @return 0 if the assertion passes */ int assert_reverse(const char *string) { char *copy = strdup(string); reverse_string(copy); reverse_string(copy); assert(strcmp(string, copy) == 0); free(copy); return 0; } // 2nd challenge, application void invert_test(void) { char test1[] = " edoc fo esuoH oT emocleW "; char test2[] = " uoy pleh lliw ti ;3 melborp ni noitcnuf siht esU "; char test3[] = " Hello World "; char test4[] = "G"; printf(" Before : %s\n", test1); reverse_string(test1); printf(" After : %s\n\n", test1); assert_reverse(test1); printf(" Before : %s\n", test2); reverse_string(test2); printf(" After : %s\n\n", test2); assert_reverse(test2); printf(" Before : %s\n", test3); reverse_string(test3); printf(" After : %s\n\n", test3); assert_reverse(test3); printf(" Before : %s\n", test4); reverse_string(test4); printf(" After : %s\n\n", test4); assert_reverse(test4); } // 3rd challenge /** * @brief Function to reverse the words in a string * * @details This function reverses the words in a string by first splitting the * string into words, then reversing each word, and finally reversing the entire * string. * * @param `str` The string to reverse the words of */ void reverse_words(char str[]) { // Algorithm is straight forward int i = 0, j = 0, pos = 0, glob = 0; char word[20][20] = {' '}; // Dirty fix, should be dynamic using malloc // clang-format off // INFO: malloc function to use: (char **)malloc((count + 1) * sizeof(char*)); // but I don't feel like manipulating pointers too much. (I use C++ more) // clang-format on printf("%s", word[0]); // Get the lenght of the string and loop around, then split the words. for (i = 0; i < my_strlen(str); i++) { if ((str[i] == ' ') || (str[i] == '\0')) { word[pos][j] = '\0'; pos++, j = 0; } else { word[pos][j] = str[i]; j++; } } // For each word, reverse it for (i = 0; i <= pos; i++) { reverse_string(word[i]); } // Then reverse the entire string for (i = 0; i <= pos; i++) { for (j = 0; j < my_strlen(word[i]); j++) { str[glob] = word[i][j]; glob++; } if (i != pos) { printf(" "); } glob++; } reverse_string(str); } /** * @brief Function to assert the reverse of a string * * @details This function asserts that the reverse of a string is equal to the * original string. * * @param `string` The string to assert the reverse of * * @return 0 if the assertion passes */ int assert_reverse_words(const char *string) { char *copy = strdup(string); // We don't want to alter the original string reverse_words(copy); reverse_words(copy); assert(strcmp(string, copy) == 0); free(copy); return 0; } // 3rd challenge, application void reverse_test(void) { char test1[] = "The dragons are coming"; char test2[] = "code love I"; char test3[] = "G"; printf("Before : %s\n", test1); reverse_words(test1); printf("After : %s\n\n", test1); assert_reverse_words(test1); printf("Before : %s\n", test2); reverse_words(test2); printf("After : %s\n\n", test2); assert_reverse_words(test2); printf("Before : %s\n", test3); reverse_words(test3); printf("After : %s\n\n", test3); assert_reverse_words(test3); // HACK: dirty scren cleanup printf("\n\n"); } /** * @brief Function to check if two characters are matching brackets * * @note helper function for `isValid()` * * @param `c1` The first character * * @param `c2` The second character * * @return true if the characters are matching brackets, false otherwise */ bool checkMatch(char c1, char c2) { if (c1 == '(' && c2 == ')') return true; if (c1 == '[' && c2 == ']') return true; if (c1 == '{' && c2 == '}') return true; return false; } // Fourth challenge /** * @brief Function to check if a string has valid brackets * @details This function checks if a string has valid brackets by using a stack * to keep track of the opening brackets. When a closing bracket is found, the * top of the stack is checked to see if it matches the closing bracket. If the * stack is empty or the brackets do not match, the function returns false. * * -------------------------------------------------------------- * * theoretical visualisation: (| | are the "comments") * [{(})] -> | [ stored | [{(})] -> | ( stored | [{(})] -> | ) matches | [{(}] * -> | } stored | [{(}] -> | ] matches | [{(] -> | false * * @param `s` The string to check * * @return true if the string has valid brackets, false otherwise */ bool isValid(const char *s) { char stack[100]; // gross way of allocating memory int top = -1; // below 0, so we can check if it's empty // Keep iterating till the end of the string for (int i = 0; s[i] != '\0'; i++) { char c = s[i]; // Get the character at the current index // If the character is an opening bracket, push it onto the stack if (c == '(' || c == '{' || c == '[') { // WARN: As with static memory allocation, this shouldn't be done on real // code. Besides, *just use C++ for real work*. // If there is an overflow, return false if (top >= 100 - 1) { return false; } stack[++top] = c; // Same thing goes here, but we check if the closing bracket matches the // top } else if (c == '}' || c == ']' || c == ')') { // If there is a mismatch or it's not empty, return false if (top == -1 || !checkMatch(stack[top], c)) { return false; } top--; } } // Debugging statement // printf("Top: %d\n", top); // If the stack is empty, the string has valid brackets return top == -1; } /** * @brief Function to assert the validity of a string * * @details This function asserts that the validity of a string is equal to the * expected value. * * @param `string` The string to assert the validity of * * @param `expected` The expected value * * @return 0 if the assertion passes */ int assert_brackets(const char *string, bool expected) { // Actually i don't know why we i added this, most likely for the sake of // consistency assert(isValid(string) ? expected : !expected); return 0; } // Fourth challenge, application void check(void) { const char *test1 = "()"; const char *test2 = "[{() }]"; const char *test3 = "{[( a+b) * x}"; const char *test4 = "{[ a+b ]*( x/y)}"; printf("Test 1: %s\n", test1); printf("Is valid : %s\n", isValid(test1) ? "Yes" : "No"); assert_brackets(test1, true); printf("Test 2: %s\n", test2); printf("Is valid : %s\n", isValid(test2) ? "Yes" : "No"); assert_brackets(test2, true); printf("Test 3: %s\n", test3); printf("Is valid : %s\n", isValid(test3) ? "Yes" : "No"); assert_brackets(test3, false); printf("Test 4: %s\n", test4); printf("Is valid : %s\n", isValid(test4) ? "Yes" : "No"); assert_brackets(test4, true); } // Global test function of all challenges int main(void) { printf("----------[1]----------\n"); strlen_test(); printf("----------[2]----------\n"); invert_test(); printf("----------[3]----------\n"); reverse_test(); printf("----------[4]----------\n"); check(); printf("--------[BONUS]--------\n"); printf("Check the bonus/main.c file for the bonus challenge.\n"); return 0; }