2025-03-10 08:55:21 +00:00

373 lines
9.7 KiB
C

#include <assert.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h> // Not used in the challenge, only for assertions.
#include <string.h>
// 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;
}