diff --git a/Week 4/README.md b/Week 4/README.md new file mode 100644 index 0000000..4d26a88 --- /dev/null +++ b/Week 4/README.md @@ -0,0 +1,3 @@ +# Week 3 + +Lab sheet: https://web.archive.org/web/20210813101313/https://teaching.csse.uwa.edu.au/units/CITS2002/labsheets/labsheet3.php diff --git a/Week 4/easy_tasks/palindrome.c b/Week 4/easy_tasks/palindrome.c new file mode 100644 index 0000000..228b803 --- /dev/null +++ b/Week 4/easy_tasks/palindrome.c @@ -0,0 +1,39 @@ +/* +* A palindrome is a word that reads the same forwards as it does backwards. For + example, the words noon and madam are palindromes. Write a function named + isPalindrome() which determines if a string, supplied as the single character + array argument to the function, is a palindrome or not, returning a Boolean. + Use the strlen() function to determine the length of the argument string. +*/ + +#include +#include +#include +#include + +bool isPalindrome(char const str[]) { + int str_length = strlen(str); + for (size_t i = 0; i < str_length/2; i++) + { + if (str[i] != str[str_length-i-1]) { + return false; + } + } + return true; +} + +int main(int argc, char const *argv[]) +{ + for (size_t i = 1; i < argc; i++) + { + if (isPalindrome(argv[i])) + { + printf("yes "); + } else { + printf("no "); + } + } + printf("\n"); + exit(EXIT_SUCCESS); + return 0; +} diff --git a/Week 4/easy_tasks/passwd.c b/Week 4/easy_tasks/passwd.c new file mode 100644 index 0000000..4b320db --- /dev/null +++ b/Week 4/easy_tasks/passwd.c @@ -0,0 +1,52 @@ +/* +* A computer password is often consider "safe" (i.e. hard to guess) if it + contains a mixture of uppercase and lowercase characters and digits. Write a + function named isSafe() to determine if a potential password (a string) has at + least two uppercase characters, two lowercase characters, and two digits. Your + function should take a single character array as its argument and return a + Boolean value to indicate whether the password is considered safe or not. +* See the online documentation (man pages) for help with the isalpha(), + islower(), and isupper() functions. Include the appropriate header file + into your program. +*/ + +#include +#include +#include +#include + +bool isSafe(char const passwd[]) { + size_t i = 0; + unsigned int uppercase = 0; + unsigned int lowercase = 0; + unsigned int digit = 0; + while (passwd[i] != '\0') + { + if (isalpha(passwd[i])) { + if (isupper(passwd[i])) { + uppercase++; + } else { + lowercase++; + } + } else { + digit++; //Assume non-alphabet characters as digits + } + i++; + } + return uppercase > 1 && lowercase > 1 && digit > 1; +} + +int main(int argc, char const *argv[]) +{ + for (size_t i = 1; i < argc; i++) + { + if (isSafe(argv[i])) { + printf("safe "); + } else { + printf("unsafe "); + } + } + printf("\n"); + exit(EXIT_SUCCESS); + return 0; +} diff --git a/Week 4/easy_tasks/rand.c b/Week 4/easy_tasks/rand.c new file mode 100644 index 0000000..758f3ca --- /dev/null +++ b/Week 4/easy_tasks/rand.c @@ -0,0 +1,99 @@ +/* +* Each call to the standard C11 function rand() returns a different random + integer. Running the same program multiple times results in exactly the same + sequence of random integers. While this is generally unexpected ("hey, they + are not random"!), it is very helpful for debugging programs without the + randomness. +* We can provide each execution with a more random sequence of random numbers by + seeding the random number generator with the C statement srand( time(NULL) ); + * Write a simple program to fill an array of 10 integers with random numbers + from rand(). Run the program several times, printing the contents of the + array. + * Now, use srand() to seed the generation of random numbers. Run the program + several times, printing the contents of the array. + * Extend your program by passing the initialised array to another function + which finds and prints the largest value in the array. + * Finally, extend the program's function to place the array's largest value + into the array's first element (index position 0), "pushing" all other + values down in the array (0→1, 1→2, 2→3, and so on). +*/ + +#include +#include +#include +#include + +#define ARRAY_LENGTH 10 + +void randomize_array(int arr_len, int arr[]) +{ + for (size_t i = 0; i < arr_len; i++) + { + arr[i] = rand(); + } +} + +int max(int arr_len, int arr[]) +{ + int max = -__INT32_MAX__; + for (size_t i = 0; i < arr_len; i++) + { + if (arr[i] > max) + { + max = arr[i]; + } + } + return max; +} + +int count(int arr_len, int arr[], int n) { + int count = 0; + for (size_t i = 0; i < arr_len; i++) + { + if (arr[i] == n) + { + count++; + } + + } + return count; +} + +// Implementation will account for very rare occurence of duplicate max values. +// (even though it is very, VERY unlikely). +int shift_max(int arr_len, int arr[]) { + int arr_[arr_len]; + memcpy(arr_, arr, sizeof(int)*arr_len); + int m = max(arr_len, arr); + int m_count = count(arr_len, arr, m); + int idx = m_count; + for (size_t i = 0; i < m_count; i++) + arr[i] = m; + for (size_t i = 0; i < arr_len; i++) + { + if (arr_[i] != m) + { + arr[idx] = arr_[i]; + idx++; + } + } +} + +int main(void) +{ + srand(time(NULL)); + int rand_arr[ARRAY_LENGTH]; + + randomize_array(ARRAY_LENGTH, rand_arr); + shift_max(ARRAY_LENGTH, rand_arr); + + printf("Max: %d\n", max(ARRAY_LENGTH, rand_arr)); + for (size_t i = 0; i < ARRAY_LENGTH; i++) + { + printf("%d ", rand_arr[i]); + } + printf("\n"); + + exit(EXIT_SUCCESS); + return 0; +} diff --git a/Week 4/easy_tasks/strcmp.c b/Week 4/easy_tasks/strcmp.c new file mode 100644 index 0000000..657cc9e --- /dev/null +++ b/Week 4/easy_tasks/strcmp.c @@ -0,0 +1,63 @@ +/* +* Write a function named my_strcmp() to determine if one string is + (lexicographically, or alphabetically) less than, equal to, or greater than + another string. Your function should accept the two character arrays as its + arguments, and return either: -1 if the first string is less than the second + string, 0 if the two strings are equal, or 1 if the first string is greater + than the second string. +* Call your function from the main() function with the code: + my_strcmp(argv[1], argv[2]). +*/ + +#include +#include +#include +#include + +// Note that in many programming languages, uppercase characters have a lower +// value than lowercase characters. +int alphabet_index(char c) { + if (isalpha(c)) + { + if (isupper(c)) { + return c - 'A'; + } else { + return c - 'a' + 26; + } + } + return 0; +} + +int my_strcmp(char s1[], char s2[]) { + int s1_len = strlen(s1); + int s2_len = strlen(s2); + size_t i = 0; + while (s1[i] != '\0' && s2[i] != '\0') + { + int s1_idx = alphabet_index(s1[i]); + int s2_idx = alphabet_index(s2[i]); + if (s1_idx > s2_idx) { + return 1; + } else if (s1_idx < s2_idx) { + return -1; + } + i++; + } + if (s1_len > s2_len) { + return 1; + } else if (s2_len > s1_len) { + return -1; + } + return 0; +} + +int main(int argc, char *argv[]) +{ + if (argc != 3) + { + printf("Only two strings for comparison allowed\n"); + exit(EXIT_FAILURE); + } + printf("%d\n",my_strcmp(argv[1], argv[2])); + return 0; +} diff --git a/Week 4/easy_tasks/strlen.c b/Week 4/easy_tasks/strlen.c new file mode 100644 index 0000000..cd2d80f --- /dev/null +++ b/Week 4/easy_tasks/strlen.c @@ -0,0 +1,31 @@ +/* +* Write a function named my_strlen() that calculates and returns the length of a + string. Your function should take one argument, a character array that + represents the string, and return an integer - the length of the string. The + calling function (the main() function) should print the integer returned by + your my_strlen() function. +* Test your function with some string constants and by passing to it some + command-line arguments. +*/ + +#include +#include + +int my_strlen(char const str[]) { + int i = 0; + while (str[i] != '\0') + { + i++; + } + return i; +} + +int main(int argc, char const *argv[]) +{ + for (size_t i = 1; i < argc; i++) + { + printf("%d ", my_strlen(argv[i])); + } + printf("\n"); + return 0; +} diff --git a/Week 4/multistat/stat.c b/Week 4/multistat/stat.c new file mode 100644 index 0000000..544b54f --- /dev/null +++ b/Week 4/multistat/stat.c @@ -0,0 +1,42 @@ +/* +* 🌶 Lecture 6 introduced C11's structures and presented an example of how + application programs can request and receive information from the underlying + operating system The example involved the gettimeofday() system-call and the + struct timeval structure. +* Similarly, applications can determine information about a file's attributes + using the stat() system-call and the struct stat structure. Note that we need + to read Section 2 of the online manual: + + prompt> man 2 stat + + otherwise we'll receive the documentation from Section 1. + +* Write a program which accepts a number of filenames on the command-line, and + prints (just as an integer) the modification-time of each file. +* Now, extend the program to also print each file's size (in bytes) and the + (more useful string) modification-time of each file, using the ctime() + function. +*/ + +#include +#include +#include +#include +#include + +int main(int argc, char const *argv[]) +{ + struct stat *stat_result; + stat_result = malloc(sizeof(struct stat)); + for (size_t i = 1; i < argc; i++) + { + stat(argv[i], stat_result); + const time_t *mod_time = &(stat_result->st_mtim).tv_sec; + char *time_human_readable = ctime(mod_time); + printf("File \"%s\":\n", argv[i]); + printf(" => last_modified: %s", time_human_readable); + printf(" => size: %ld\n\n", stat_result->st_size); + } + + return 0; +} diff --git a/Week 4/replace/replace.c b/Week 4/replace/replace.c new file mode 100644 index 0000000..c28bf7a --- /dev/null +++ b/Week 4/replace/replace.c @@ -0,0 +1,118 @@ +/* +* 🌶 Write a function named replace() that replaces all instances of one string + for another string in a third string. For example: + + prompt> ./replace red blue aredrobin + abluerobin + + prompt> ./replace cat bison catocathartic + bisonobisonhartic + +* A reasonable prototype for the function is: + void replace( char oldword[], char newword[], char whole_sentence[] ); +* Ensure you have terminated your string correctly. +*/ + +#include +#include +#include +#include + +bool __match(int m_len, char m[], char s_len, char s[], int start) +{ + int offset = start + m_len; + if (offset <= s_len) + { + int idx = 0; + for (int i = start; i < offset; i++) + { + if (m[idx] != s[i]) + { + return false; + } + idx++; + } + return true; + } + return false; +} + +// assume match and replacement length > 0 +void replace(char match[], char replacement[], char string[]) +{ + int match_length = strlen(match); + int replacement_length = strlen(replacement); + int string_length = strlen(string); + int positions[string_length]; // easy solution - probably memory wasteful + int valid_positions[string_length]; + memset(positions, -1, sizeof positions); + memset(valid_positions, -1, sizeof valid_positions); + + int idx = 0; + int idx_v = 0; + int idx_new = 0; + int last_position = -__INT32_MAX__; + + // get possible positions. + for (int i = 0; i < string_length; i++) + { + if (string[i] == match[0]) + { + positions[idx] = i; + idx++; + } + } + + // get valid positions for replacement. + idx = 0; + do + { + int position = positions[idx]; + if (__match(match_length, match, string_length, string, position)) + { + // prevent overlapping matches + if (position > last_position + match_length) { + valid_positions[idx_v] = position; + last_position = position; + idx_v++; + } + } + idx++; + } while (positions[idx] != -1); + + // make new string + char new_string[string_length + idx_v*(match_length - replacement_length)]; + idx = 0; + idx_v = 0; + while (string[idx] != '\0') + { + if (idx == valid_positions[idx_v]) { + idx_v++; + idx += match_length-1; + for (size_t i = 0; i < replacement_length; i++) + { + new_string[idx_new] = replacement[i]; + idx_new++; + } + } else { + new_string[idx_new] = string[idx]; + idx_new++; + } + idx++; + } + new_string[idx_new] = '\0'; + strcpy(string, new_string); +} + +int main(int argc, char *argv[]) +{ + if (argc != 4) + { + printf("Error - only three arguments allowed\nreplace pattern replacement string\n"); + exit(EXIT_FAILURE); + } + + replace(argv[1], argv[2], argv[3]); + printf("%s\n", argv[3]); + return 0; +} diff --git a/Week 4/superloop/superloop.c b/Week 4/superloop/superloop.c new file mode 100644 index 0000000..b40f371 --- /dev/null +++ b/Week 4/superloop/superloop.c @@ -0,0 +1,65 @@ +/* + * 🌶 🌶 🌶 If you had to write some code that iterated through all the + possibilities for three variables in the range 0 to 10, you would probably + write code similar to: + + for(int a = 0; a < 10; ++a) { + for(int b = 0; b < 10; ++b) { + for(int c = 0; c < 10; ++c) { + ; // LOOP-BODY USING a, b, AND c + } + } + } + But what if you had to extend this to 4, 5, or even 10+ "nested" loops? + Instead of further nesting more loops, it is possible to write a function that + acts as a "superloop", performing the equivalent of n nested loops with just + one loop. Write a function to do this, taking an argument n that indicates the + number of nested loops your function should perform. To do this, you will need + to keep a 1-dimensional array of n values that maintain the state of each loop + during the execution of your function. +*/ + +#include +#include "superloop.h" + +struct __superloop_state { + void (*function)(int, int (*)[]); + int (*value)[]; + int level; + int depth; + int start; + int end; + int increment; +}; + +void __loop(struct __superloop_state *state) +{ + if (state->level < state->depth) + { + for (int i = state->start; i < state->end; i += state->increment) + { + (*state->value)[state->level] = i; + state->level++; + __loop(state); + state->level--; + } + } else { + (*state->function)(state->depth, state->value); + } +} + +void superloop(int depth, int start, int end, int increment, void (*function)(int, int (*)[])) +{ + int (*value)[depth]; + value = malloc(depth*sizeof(int)); + struct __superloop_state state = { + function, + value, + 0, + depth, + start, + end, + abs(increment) + }; + __loop(&state); +} \ No newline at end of file diff --git a/Week 4/superloop/superloop.h b/Week 4/superloop/superloop.h new file mode 100644 index 0000000..6e01394 --- /dev/null +++ b/Week 4/superloop/superloop.h @@ -0,0 +1,6 @@ +#ifndef _SUPERLOOP_H +#define _SUPERLOOP_H + +extern void superloop(int depth, int start, int end, int increment, void (*function)(int, int (*)[])); + +#endif \ No newline at end of file diff --git a/Week 4/superloop/test_superloop.c b/Week 4/superloop/test_superloop.c new file mode 100644 index 0000000..7d11999 --- /dev/null +++ b/Week 4/superloop/test_superloop.c @@ -0,0 +1,28 @@ +// see superloop.c for information +// remember to add ./superloop.c to gcc for this to compile! + +#include +#include +#include "superloop.h" + + +void func(int length, int (*values)[]) { + for (size_t i = 0; i < length; i++) + { + printf("%d ", (*values)[i]); + } + printf("\n"); +} + +int main(int argc, char const *argv[]) +{ + if (argc != 5) + { + printf("Expected 4 arguments\ntest_superloop \n"); + exit(EXIT_FAILURE); + } + + superloop(atoi(argv[1]), atoi(argv[2]), atoi(argv[3]), atoi(argv[4]), func); + exit(EXIT_SUCCESS); + return 0; +}