Programming Fundamentals

Q1

// Written by Angela Finlayson
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

struct node {
    struct node *next;
    int          data;
};

int sum_divisible(struct node *head1, struct node *head2);
struct node *strings_to_list(int len, char *strings[]);

int main(int argc, char *argv[]) {
    // create two linked lists from command line arguments
    int dash_arg = argc - 1;
    while (dash_arg > 0 && strcmp(argv[dash_arg], "-") != 0) {
        dash_arg = dash_arg - 1;
    }
    struct node *head1 = strings_to_list(dash_arg - 1, &argv[1]);
    struct node *head2 = strings_to_list(argc - dash_arg - 1, &argv[dash_arg + 1]);

    int result = sum_divisible(head1, head2);
    printf("%d\n", result);

    return 0;
}


// sum the elements in list1 that are divisible by the 
// the corresponding element in list2
// if one list is longer than the other, the extra list elements are ignored 
int sum_divisible(struct node *head1, struct node *head2) {
	if (!head1 || !head2) {
		return 0;
	} else {
		if(head1->data % head2->data == 0) 
			return head1->data + sum_divisible(head1->next, head2->next);
		else 
			return sum_divisible(head1->next, head2->next);
	}
}



// create linked list from array of strings
struct node *strings_to_list(int len, char *strings[]) {
    struct node *head = NULL;
    for (int i = len - 1; i >= 0; i = i - 1) {
        struct node *n = malloc(sizeof (struct node));
        assert(n != NULL);
        n->next = head;
        n->data = atoi(strings[i]);
        head = n;
    }
    return head;
}

Q2

#include <stdio.h>

#define MAX_COLS 100

char min_lowercase(int size, char array[MAX_COLS]);

// This is a simple main function which could be used
// to test your min_lowercase function.
// It will not be marked.
// Only your min_lowercase function will be marked.
int main(int argc, char *argv[]) {
    char test_array[MAX_COLS] = {'a', 'B', 'c', 'D', '!'};

    int result = min_lowercase(5, test_array);
    printf("%c\n", result);

    return 0;
}

// Returns the lowercase character with the lowest ASCII value from the array.
char min_lowercase(int size, char array[MAX_COLS]) {
    char min_letter = 'z';

    for (int i = 0; i < size; i++) {
        if (array[i] >= 'a' && array[i] <= 'z' && array[i] < min_letter) {
            min_letter = array[i];
        }
    }

    return min_letter;
}

Q3

// Written by Tammy Zhong
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>

struct node {
    struct node *next;
    char          data;
};

struct node *insert_alphabet_after(char ch, struct node *head);
struct node *strings_to_list(int len, char *strings[]);
void print_list(struct node *head);

struct node *insert_alphabet_after(char ch, struct node *head) {
     if (head == NULL) {
        struct node *new = malloc(sizeof(struct node));
        new->data = ch;
        new->next = NULL; 
        return new;
    }

    struct node *current = head;
    while (current != NULL) {
        if (current->data + 1 == ch) {
            struct node *new = malloc(sizeof(struct node));
            new->data = ch;
            new->next = NULL; 
            new->next = current->next;
            current->next = new;

        } 
        current = current->next;
    }
    return head;
}

int main(int argc, char *argv[]) {
    char value;
    scanf(" %c", &value);
    // create linked list from command line arguments
    struct node *head = NULL;
    if (argc > 1) {
        // list has elements
        head = strings_to_list(argc - 1, &argv[1]);
    }

    struct node *new_head = insert_alphabet_after(value, head);
    print_list(new_head);

    return 0;
}


// create linked list from array of strings
struct node *strings_to_list(int len, char *strings[]) {
    struct node *head = NULL;
    int i = len - 1;
    while (i >= 0) {
        struct node *n = malloc(sizeof (struct node));
        assert(n != NULL);
        n->next = head;
        n->data = strings[i][0];
        head = n;
        i -= 1;
    }   
    return head;
}

// print linked list
void print_list(struct node *head) {
    printf("[");    
    struct node *n = head;
    while (n != NULL) {
        // If you're getting an error here,
        // you have returned an invalid list
        printf("%c", n->data);
        if (n->next != NULL) {
            printf(", ");
        }
        n = n->next;
    }
    printf("]\n");
}

Q4

// Written by Jake Renzella
#include <stdio.h>

#define MAX_ROWS 100

int vowels_exactly_balanced(int size, char *array[MAX_ROWS]);

// This is a simple main function which could be used
// to test your vowels_exactly_balanced function.
// It will not be marked.
// Only your count_neutral_rows function will be marked.
int main(int argc, char *argv[]) {
    char *test_arrray[MAX_ROWS] = {
        "abUUcd",
        "abcdUU",
        "Abaa",
        "asdaa",
    };

    char *test_arrray_2[MAX_ROWS] = {
        "abcdaa",
        "abcda",
        "babau",
    };

    char *test_arr_3[MAX_ROWS] = {
        "fgh",
        "j",
        "zxcvbnm",
    };

    int result = vowels_exactly_balanced(4, test_arrray);
    printf("The vowels_exactly_balanced function returned: %d\n", result);

    result = vowels_exactly_balanced(3, test_arrray_2);
    printf("The vowels_exactly_balanced function returned: %d\n", result);

    result = vowels_exactly_balanced(3, test_arr_3);
    printf("%d\n", result);
    return 0;
}

int vowels_exactly_balanced(int size, char *array[MAX_ROWS]) {
    if (size <= 1) {
        // 0 or 1 row is always balanced
        return 1;
    } 

    if (size > MAX_ROWS) {
        // Error: size exceeds MAX_ROWS
        return 0;
    } 

    // Will store the first non-zero vowel count
    int target_vowels = -1; 

    for (int row = 0; row < size; row++) {
        int vowels = 0;

        for (int col = 0; array[row][col] != '\0'; col++) {
            char c = array[row][col];
            if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u' ||
                c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U') {
                vowels++;
            }
        }

        if (target_vowels == -1) {
            if (vowels > 0) {
                target_vowels = vowels;
            }
        } else if (vowels != target_vowels) {
            // Imbalance found, early return
            return 0; 
        }
    }

    return 1; // No imbalances found
}

Q5

// Written by Sofia De Bellis

// This program scans in an integer representing a month of the year.
// It prints out a message to say what season the month is in Australia.
//
// Unfortunately, this code contains a number of errors. Please help fix it!

#include <stdio.h>

int main(void) {
    int month; // BUG #0 - forgot to declare the variable

    printf("Enter the month number (1-12): ");
    scanf("%d", &month); // Bug #1 - forgot & before month

    if (month < 1 || month > 12) { // BUG #1.5 >= 12 instead of > 12
        printf("Invalid month entered.\n");
    } else if (month >= 3 && month <= 5) { // Bug #2 - use || instead of &&
        printf("It's autumn in Australia.\n");
    } else if (month >= 6 && month <= 8) {
        printf("It's winter in Australia.\n");
    } else if (month >= 9 && month <= 11) {
        printf("It's spring in Australia.\n");
    } else { // Bug #3 - forgot to handle December, January, and February
        printf("It's summer in Australia.\n");
    }

    return 0;
}

Q6

// Written by Sofia De Bellis

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>

struct node {
    // Bug: Missing semicolon using comma instead
    struct node *next;
    int          data;
};


// compare_tens should return -1 if list1 has more tens than list2
// compare_tens should return  1 if list1 has less tens than list2
// compare_tens should return  0 if list1 has the same tens as list2
int compare_tens(struct node *head1, struct node *head2) {
    
    // Bug: current1 and current2 should be a pointer
    struct node *current1 = head1;
    struct node *current2 = head2;
    int count1 = 0;
    int count2 = 0;
    
    // Bug: current->next != NULL instead of current != NULL
    while (current1 != NULL) {
        // Bug: Using > instead of >= and < instead of <=
        if (current1->data >= 10 && current1->data <= 99) {
            count1++;
        } 
        // Bug: not moving current correctly
        current1 = current1->next;
    }
        
    while (current2 != NULL) {
        // Bug: Using || instead of &&
        if (current2->data >= 10 && current2->data <= 99) {
            count2++;
        } 
        current2 = current2->next;
    }

    if (count1 < count2) {
        // Bug: Returning 1 for count1 < count2
        return -1;
    } else if (count1 > count2) {
        // Bug: Returning -1 for count1 > count2
        return 1;
    } 

    return 0;
}



// DO NOT MODIFY THE CODE BELOW THIS LINE, THIS CODE ALLOWS YOU TO 
// TAKE IN A LIST OF NUMBERS TO TEST WHETHER YOUR FUNCTION IS WORKING

struct node *strings_to_list(int len, char *strings[]);

// DO NOT CHANGE THIS FUNCTION
int main(int argc, char *argv[]) {
    // create two linked lists from command line arguments
    int dash_arg = argc - 1;
    while (dash_arg > 0 && strcmp(argv[dash_arg], "-") != 0) {
        dash_arg = dash_arg - 1;
    }
    struct node *head1 = strings_to_list(dash_arg - 1, &argv[1]);
    struct node *head2 = strings_to_list(argc - dash_arg - 1, &argv[dash_arg + 1]);

    int result = compare_tens(head1, head2);
    printf("%d\n", result);

    return 0;
}

// DO NOT CHANGE THIS FUNCTION
// create linked list from array of strings
struct node *strings_to_list(int len, char *strings[]) {
    struct node *head = NULL;
    for (int i = len - 1; i >= 0; i = i - 1) {
        struct node *n = malloc(sizeof (struct node));
        assert(n != NULL);
        n->next = head;
        n->data = atoi(strings[i]);
        head = n;
    }
    return head;
}

Q7

// Written By Sofia De Bellis

// The following code is meant to read in integers and print out the 
// factorial of that integer until the user enters Ctrl + D. 
// However, it contains a number of errors that you need to fix. Good luck!

// note: the factorial of a number is the product of all positive 
// integers less than or equal to that number.

#include <stdio.h>

void compute_factorial(int number, int *factorial); 

int main(void) {
    int num1;
    int factorial;

    printf("Enter a number: ");
    
    // Bug: scanf("%d", &num1) > 1 should be scanf("%d", &num1) == 1
    while (scanf("%d", &num1) == 1) {
        // Bug: *factorial should be &factorial
        compute_factorial(num1, &factorial); 
        // Bug: %c should be %d and %lf should be %d
        printf("The factorial of %d is %d\n", num1, factorial);
        // Bug: missing prompt
        printf("Enter a number: ");
    }
    return 0;
}

void compute_factorial(int number, int *factorial) {
    // Bug: result should be initialised to 1
    int res = 1;
    // Bug: i++ should be i--
    for (int i = number; i > 0; i--) {
        res = res * i;
    }
    // Bug: factorial should be *factorial
    *factorial = res;
    return;
}

Q8

// Written by Nicole Luong

// This program should create and scan in the elements of a 3x3 2d array, 
// then multiply all the odd numbers in the 2d array by 100.
//
// Unfortunately, this code contains a number of errors. Please help fix it!

#include <stdio.h>
#include <stdlib.h>

#define SIZE 3

int main(void) {

    printf("Enter your numbers:\n");

    int array[SIZE][SIZE]; // char instead of int
    for (int row = 0; row < SIZE; row++) {
        for (int col = 0; col < SIZE; col++) { // loop decrements
            scanf("%d", &array[row][col]); // %c instead of $d
        }
    }

    for (int row = 0; row < SIZE; row++) {
        for (int col = 0; col < SIZE; col++) {
            if (array[row][col] % 2 != 0) { // swapped with the line above and [col] is replaced by another [row], and / instead of %
                array[row][col] = array[row][col] * 100; 
            }
        }
    }

    // ==================== DO NOT NEED TO EDIT BELOW HERE =====================
    // There are no bugs in here, this is simply to assist in printing out
    // the contents of the 2D array
    printf("Converted:\n");
    for (int row = 0; row < SIZE; row++) {
        for (int col = 0; col < SIZE; col++) {
            printf("%d ", array[row][col]);
        }
        printf("\n");
    }

    return 0;
}

Q9

// Written by Sofia De Bellis
#include <stdio.h>
#include <stdlib.h>

int rotation_shift_amount(char *str1, char *str2);
int my_strlen(char *str);
void my_strcat(char *dest, char *src);
char *my_strstr(char *haystack, char *needle);

int main(void) {
    // This main function will not be used in testing.
    // You may write code for testing your rotation_shift_amount function here.
    printf("%d\n", rotation_shift_amount("waterbottle", "erbottlewat"));

    return 0;
}

// Returns the shift amount needed to rotate s1 to s2
int rotation_shift_amount(char *str1, char *str2) {
    // Get the length of both strings
    int len1 = my_strlen(str1);
    int len2 = my_strlen(str2);

    // If lengths are not equal, s2 cannot be a rotation of s1
    if (len1 != len2) {
        return -1;
    }

    if (len1 == 0 && len2 == 0) {
        return 0;
    }

    // Create a new string that is s1 concatenated with itself
    char str1_str1[2 * len1 + 1];
    for (int i = 0; i < len1; i++) {
        str1_str1[i] = str1[i];
    }
    str1_str1[len1] = '\0';
    my_strcat(str1_str1, str1);

    // Find the position of s2 in the concatenated string
    char *pos = my_strstr(str1_str1, str2);
    if (pos != NULL) {
        // Calculate the shift amount
        int shift_amount = pos - str1_str1;
        return shift_amount % len1;
    } else {
        return -1;
    }
}

// Returns the length of a string
int my_strlen(char *str) {
    int i = 0;
    while (str[i] != '\0') {
        i++;
    }
    return i;
}

// Concatenates src to dest
void my_strcat(char *dest, char *src) {
    int i = my_strlen(dest);
    int j = 0;
    while (src[j] != '\0') {
        dest[i] = src[j];
        i++;
        j++;
    }
    dest[i] = '\0';
}

// Returns a pointer to the first occurrence of the substring needle in the string haystack
char *my_strstr(char *haystack, char *needle) {
    int i = 0;
    int j = 0;
    while (haystack[i] != '\0') {
        if (haystack[i] == needle[j]) {
            while (needle[j] != '\0' && haystack[i] == needle[j]) {
                i++;
                j++;
            }
            if (needle[j] == '\0') {
                return &haystack[i - j];
            }
            i -= j;
            j = 0;
        }
        i++;
    }
    return NULL;
}

Q10

// Written by Ben Briant

#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define MAX_LEN 20

enum order_type {
    BUY,
    SELL
};
struct order {
    enum order_type type;
    int price;
    int quantity;
    struct order *next;
};

struct order *read_orders(void);
void match_orders(struct order *a, struct order *b) {
    int lower;
    if (a->quantity < b->quantity) {
        lower = a->quantity;
    } else {
        lower = b->quantity;
    }

    a->quantity -= lower;
    b->quantity -= lower;
}

void insert(struct order **orders, struct order *ins) {
    if (*orders == NULL) {
        *orders = ins;
        return;
    }
    struct order *prev = NULL;
    struct order *curr = *orders;
    while (curr) {
        if (ins->price > curr->price) {
            if (!prev) {
                *orders = ins;
            } else {
                prev->next = ins;
            }
            ins->next = curr;
            return;
        }
        prev = curr;
        curr = curr->next;
    }
    prev->next = ins;
    ins->next = NULL;
}

void handle_buy(
    struct order **bids, struct order **asks, struct order *buy_order) {
    assert(buy_order->type == BUY);

    struct order *prev = NULL;
    struct order *ask = *asks;
    while (ask && buy_order->quantity > 0) {
        struct order *next = ask->next;
        if (ask->price <= buy_order->price) {
            match_orders(ask, buy_order);
        }
        if (ask->quantity <= 0) {
            // remove ask
            if (prev) {
                prev->next = ask->next;
            } else {
                *asks = ask->next;
            }
            free(ask);
            ask = prev;
        }
        prev = ask;
        ask = next;
    }

    if (buy_order->quantity > 0) {
        insert(bids, buy_order);
    }
}

// Yes, this is the same as `handle_buy`, but with a sign flipped.
void handle_ask(
    struct order **bids, struct order **asks, struct order *sell_order) {
    assert(sell_order->type == SELL);

    struct order *prev = NULL;
    struct order *bid = *bids;
    while (bid && sell_order->quantity > 0) {
        struct order *next = bid->next;
        if (bid->price >= sell_order->price) {
            match_orders(sell_order, bid);
        }
        if (bid->quantity <= 0) {
            // remove bid
            if (prev) {
                prev->next = bid->next;
            } else {
                *bids = bid->next;
            }
            free(bid);
            bid = prev;
        }
        prev = bid;
        bid = next;
    }

    if (sell_order->quantity > 0) {
        insert(asks, sell_order);
    }
}

void print_side(struct order *asks, char *type) {
    if (!asks) {
        return;
    }
    printf("%s %d items for $%d\n", type, asks->quantity, asks->price);
    print_side(asks->next, type);
}

void free_orders(struct order *order) {
    if (!order) {
        return;
    }
    free_orders(order->next);
    free(order);
}

int main(void) {
    struct order *order_list = read_orders();
    struct order *bids = NULL;
    struct order *asks = NULL;

    struct order *curr_order = order_list;

    while (curr_order) {
        struct order *next = curr_order->next;
        curr_order->next = NULL;

        if (curr_order->type == BUY) {
            handle_buy(&bids, &asks, curr_order);
        } else if (curr_order->type == SELL) {
            handle_ask(&bids, &asks, curr_order);
        }

        curr_order = next;
    }
    print_side(asks, "SELL");
    print_side(bids, "BUY");

    free_orders(bids);
    free_orders(asks);

    return 0;
}

// This function reads in a list of orders, ending with either
// a blank line, or EOF.
// Returns: A pointer to the head of the linked list
struct order *read_orders(void) {
    char line[MAX_LEN];
    struct order *head = NULL;
    struct order *tail = NULL;
    while (fgets(line, MAX_LEN, stdin) != NULL && strcmp(line, "\n")) {
        struct order *new = malloc(sizeof(struct order));
        new->next = NULL;
        char order_char;
        sscanf(line, "%c %d %d", &order_char, &new->quantity, &new->price);
        new->type = (order_char == 'b') ? BUY : SELL;
        if (head != NULL) {
            tail->next = new;
            tail = new;
        } else {
            head = tail = new;
        }
    }
    return head;
}

Q11

// Written by Sofia De Bellis & Ben Briant

#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>

#define MAX_LEN 1000
struct executable {
    char executable_name[MAX_LEN];
    struct file *files;
    struct executable *next;
};

struct file {
    char file_name[MAX_LEN];
    struct file *next;
};

bool endswith_c(char *str) {
    if (strlen(str) < 3) return false;
    return strcmp(str + strlen(str) - 2, ".c") == 0;
}

bool startswith(char *str, char *prefix) {
    char tmp_str[strlen(str) + 1];
    strcpy(tmp_str, str);
    char *dot = tmp_str;

    while (*dot != '.' && *dot != '\0') {
        dot++;
    }

    if (*dot == '\0') return false;
    *dot = '\0';

    return strcmp(tmp_str, prefix) == 0 && endswith_c(str);
}

void remove_newline(char *str) {
    if (str[strlen(str) - 1] == '\n') {
        str[strlen(str) - 1] = '\0';
    }
}

char **tokenize_line(char *line, int *len) {
    remove_newline(line);
    char **tokens = NULL;
    int i = 0;

    for (char *token = strtok(line, " "); token != NULL;
         token = strtok(NULL, " ")) {
        tokens = realloc(tokens, (i + 1) * sizeof(char *));
        tokens[i] = token;
        i++;
    }
    *len = i;
    return tokens;
}

bool prefix(const char *pre, const char *str) {
    return strncmp(pre, str, strlen(pre)) == 0;
}

int main(void) {
    char line[MAX_LEN];

    struct executable *executables = NULL;
    while (printf("# ") && fgets(line, MAX_LEN, stdin)) {
        int args_len;
        char **args = tokenize_line(line, &args_len);
        if (strcmp(args[0], "dcc") == 0) {
            // Validate command
            // Now, every following argument must be either a `.c` file, or
            // '-o' followed by an executable name
            char **dcc_argv = &args[1];
            int dcc_argc = args_len - 1;
            bool found_o = false;
            bool valid_command = true;
            bool found_exec = false;
            char *executable;
            for (int i = 0; i < dcc_argc && valid_command; i++) {
                if (endswith_c(&dcc_argv[i][0])) {
                    continue;
                } else if (!strcmp(dcc_argv[i], "-o")) {
                    if (found_o) {
                        // multiple '-o'
                        valid_command = false;
                    }
                    found_o = true;
                    if (i + 1 >= dcc_argc) {
                        // No argument after '-o'
                        valid_command = false;
                    }
                    i++;
                    executable = dcc_argv[i];
                    if (endswith_c(executable)) {
                        // Executable is a c file
                        valid_command = false;
                    }
                    bool executable_matches = false;
                    for (int j = 0; j < dcc_argc; j++) {
                        if (startswith(dcc_argv[j], executable)) {
                            executable_matches = true;
                        }
                    }
                    if (!executable_matches) {
                        // Executable doesn't match a c file
                        valid_command = false;
                    }
                    found_exec = true;
                } else {
                    // Non-.c file, or executable not preceded by '-o'
                    valid_command = false;
                }
            }
            if (valid_command && found_exec) {
                printf("Executable %s compiled!\n", executable);
                struct executable *new = malloc(sizeof(struct executable));
                strcpy(new->executable_name, executable);
                new->files = NULL;
                new->next = NULL;

                if (executables) {
                    struct executable *curr = executables;
                    while (curr->next) {
                        curr = curr->next;
                    }
                    curr->next = new;
                } else {
                    executables = new;
                }

                for (int i = 0; i < dcc_argc; i++) {
                    if (strcmp(dcc_argv[i], "-o") &&
                        strcmp(dcc_argv[i], executable)) {
                        struct file *new_file = malloc(sizeof(struct file));
                        strcpy(new_file->file_name, dcc_argv[i]);
                        new_file->next = NULL;
                        if (new->files) {
                            struct file *curr_file = new->files;
                            while (curr_file->next) {
                                curr_file = curr_file->next;
                            }
                            curr_file->next = new_file;
                        } else {
                            new->files = new_file;
                        }
                    }
                }
            } else {
                printf("Invalid command!\n");
            }
            valid_command = true;
            found_o = false;
            found_exec = false;
        } else if (prefix("./", args[0])) {
            // Running executable
            char *executable = &args[0][2];
            struct executable *curr = executables;
            struct executable *to_run = NULL;
            while (curr) {
                if (strcmp(curr->executable_name, executable) == 0) {
                    to_run = curr;
                }
                curr = curr->next;
            }
            if (!to_run) {
                continue;
            }
            printf("Executable '%s' was compiled with the following files:\n",
                   executable);
            struct file *curr_file = to_run->files;
            while (curr_file) {
                printf(" - %s\n", curr_file->file_name);
                curr_file = curr_file->next;
            }
        } else {
            // should never reach this tbh
            printf("Unknown Error occured\n");
        }
    }
}