LMU ☀️ CMSI 2210
COMPUTER SYSTEMS ORGANIZATION
HOMEWORK #4 PARTIAL ANSWERS
  1. piano_keys.c
    // Writes a table of piano notes and their frequencies to stdout.
    
    #include <stdio.h>
    #include <math.h>
    
    char *KEY_NAMES[] = {"A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"};
    int STEPS = sizeof KEY_NAMES / sizeof (char*);
    int NUMBER_OF_KEYS = 88;
    double INITIAL_FREQUENCY = 27.5;
    
    int main() {
        for (int i = 0; i < NUMBER_OF_KEYS; i++) {
            char* name = KEY_NAMES[i % STEPS];
            double frequency = INITIAL_FREQUENCY * pow(2.0, (double)i / STEPS);
            printf("%s\t%10.4f\n", name, frequency);
        }
        return 0;
    }
    
  2. scales.c
    // This application takes a single command line argument which is the name of a note,
    // and prints, to stdout, the major (Ionian) and minor (Aeolian) scales starting at
    // that note.  The program is very weak; note names are constrained to be in {A, A#, B,
    // C, C#, D, D#, E, F, F#, G, G#}.
    
    #include <stdio.h>
    #include <string.h>
    
    char *KEYS[] = {"A", "A#", "B", "C", "C#", "D", "D#", "E", "F", "F#", "G", "G#"};
    int NUMBER_OF_NOTES = sizeof KEYS / sizeof (char*);
    
    int MAJOR_STEPS[] = {2, 2, 1, 2, 2, 2};
    int MINOR_STEPS[] = {2, 1, 2, 2, 1, 2};
    int NUMBER_OF_STEPS = sizeof MAJOR_STEPS / sizeof(int);
    
    void print_scale(int note_index, char* type, int steps[]) {
        printf("%s %s:", KEYS[note_index], type);
        for (int i = 0, j = 0; i <= NUMBER_OF_STEPS; j += steps[i++]) {
            printf("%3s", KEYS[(note_index + j) % NUMBER_OF_NOTES]);
        }
        printf("\n");
    }
    
    int index_of(char* string, char* strings[], size_t array_length) {
        for (int i = 0; i < array_length; i++) {
            if (strcmp(string, strings[i]) == 0) {
                return i;
            }
        }
        return -1;
    }
    
    int main(int argc, char** argv) {
        if (argc != 2) {
            printf("This program requires exactly one command line argument\n");
            return 1;
        }
    
        int note_index = index_of(argv[1], KEYS, NUMBER_OF_NOTES);
        if (note_index == -1) {
            printf("No such key: %s\n", argv[1]);
            return 2;
        }
    
        print_scale(note_index, "major", MAJOR_STEPS);
        print_scale(note_index, "minor", MINOR_STEPS);
        return 0;
    }
    
  3. silly.c
    #include <stdlib.h>
    #include <string.h>
    #include <assert.h>
    #include <stdio.h>
    
    // silly(s, t) takes in two strings (pointers to bytes in memory with a zero at the end),
    // and returns a new string made by joining up successive substrings of the arguments,
    // according to the examples below:
    //
    // silly("phone", "booth")  ⇒  "pphphophonphoneboothbootboobob"
    // silly("", "")  ⇒  ""
    // silly("abc", "")  ⇒  "aababc"
    // silly("", "abc")  ⇒  "abcaba"
    // silly("too", "real")  ⇒  "ttotoorealrearer"
    //
    // Because a new string is allocated and returned, the caller is responsible for
    // freeing the memory of the returned string.
    char* silly(char* s, char* t) {
        int length_s = strlen(s);
        int length_t = strlen(t);
    
        // Allocate space for the result
        char* result = malloc(
            (length_s * (length_s + 1)) / 2      // bytes in substrings of s
            + (length_t * (length_t + 1)) / 2    // bytes in substrings of t
            + 1);                                // one byte for the closing '\0`
    
        // Initialize to empty string because we will use strncat
        result[0] = '\0';
    
        // Put in the result characters
        for (int i = 0; i <= length_s; i++) strncat(result, s, i);
        for (int i = length_t; i >= 0 ; i--) strncat(result, t, i);
    
        return result;
    }
    
    // You only need to turn in the function, but it's a good idea to include unit tests.
    void test(char* first_string, char* second_string, char* expected) {
        char* actual = silly(first_string, second_string);
        printf("Test <%s> should equal <%s>\n", actual, expected);
        assert(strcmp(actual, expected) == 0);
        free(actual);
    }
    
    int main() {
        test("", "", "");
        test("phone", "booth", "pphphophonphoneboothbootboobob");
        test("abc", "", "aababc");
        test("", "abc", "abcaba");
        test("too", "real", "ttotoorealrearer");
        printf("%s\n", "All tests passed");
        return 0;
    }
    
  4. rotate.c
    #include <string.h>
    #include <stdlib.h>
    #include <assert.h>
    #include <stdio.h>
    
    // Returns the k-fold left-rotation of s, as a newly allocated
    // string. (That means the caller is responsible for the memory.)
    //
    // Examples:
    //    rotate_left("doghouse", 0)  ===>  "doghouse"
    //    rotate_left("doghouse", 1)  ===>  "oghoused"
    //    rotate_left("doghouse", 2)  ===>  "ghousedo"
    //    rotate_left("doghouse", 3)  ===>  "housedog"
    //    rotate_left("doghouse", 4)  ===>  "ousedogh"
    //    rotate_left("doghouse", 5)  ===>  "usedogho"
    //
    // Rotating a negative number of times returns a copy of the input, as we take the
    // meaning of, say, rotating -4 times as rotating not at all.
    char* rotate_left(char* s, int k) {
        int n = strlen(s);
    
        // Handle non-positive k as well as the empty string.
        if (k <= 0 || n == 0) return strdup(s);
    
        // Ensure 0 <= k < n. This is one reason we checked for n==0 above.
        if (k >= n) k %= n;
    
        // Allocate space for the result.
        char* result = malloc(n + 1);
    
        // Put in the result characters.
        int i = 0;
        for (int j = k; j < n; j++) result[i++] = s[j];
        for (int j = 0; j < k; j++) result[i++] = s[j];
    
        // Insert the string terminator and return.
        result[n] = '\0';
        return result;
    }
    
    // You only need to turn in the function, but it's a good idea to include unit tests.
    void test(char* input_string, int length, char* expected) {
        char* actual = rotate_left(input_string, length);
        printf("Test <%s> should equal <%s>\n", actual, expected);
        assert(strcmp(actual, expected) == 0);
        free(actual);
    }
    
    int main() {
        test("doghouse", -5, "doghouse");
        test("doghouse", -50, "doghouse");
        test("doghouse", 0, "doghouse");
        test("doghouse", 8, "doghouse");
        test("doghouse", 16, "doghouse");
        test("doghouse", 24, "doghouse");
        test("doghouse", 32, "doghouse");
        test("doghouse", 800000, "doghouse");
        test("doghouse", 1, "oghoused");
        test("doghouse", 2, "ghousedo");
        test("doghouse", 3, "housedog");
        test("doghouse", 4, "ousedogh");
        test("doghouse", 5, "usedogho");
        test("doghouse", 6, "sedoghou");
        test("doghouse", 7, "edoghous");
        test("doghouse", 11, "housedog");
        test("doghouse", 19, "housedog");
        test("", 0, "");
        test("", 1, "");
        test("", 60000, "");
        test("", -20, "");
        test("x", -20, "x");
        test("x", 0, "x");
        test("x", 20, "x");
        test("x", 5000, "x");
    
        printf("All tests passed\n");
        return 0;
    }
    
  5. utf32toutf8.c
    // A C filter (stdin -> stdout) that converts a utf-32 encoded byte stream to
    // the equivalent utf-8 byte stream. Only codepoints in 0..10FFFF are allowed;
    // codepoints beyond this range, as well as any other codepoints explicitly
    // designated as unmapped in Unicode are considered illegal and will be turned
    // into U+FFFD REPLACEMENT CHARACTERs.
    //
    // The return value of the process will be 0 if and only if every character was
    // properly converted, and 1 otherwise.
    
    #include <stdio.h>
    #include <stdint.h>
    #include <stdbool.h>
    
    int returnCode = 0;
    
    // Returns whether the given codepoint is in the acceptable range for Unicode
    // and not designated a non-character.  In other words it has to be in the
    // range 0...10FFFF and NOT be in the range FDD0...FDEF nor end with FFFE or
    // FFFF.
    static int legal(uint32_t codepoint) {
        return codepoint <= 0x0010FFFF
            && (codepoint < 0x0000FDD0 || codepoint > 0x0000FDEF)
            && (codepoint & 0x0000FFFE) != 0x0000FFFE;
    }
    
    // Writes to standard output the UTF-8 byte sequence corresponding to the given
    // codepoint. If the codepoint is not legal, outputs the UTF-8 sequence for the
    // REPLACEMENT CHARACTER.
    static void emit_utf8_for_codepoint(uint32_t c) {
        if (!legal(c)) {
            // Illegal char: mark fail and emit U+FFFD REPLACEMENT CHARACTER in UTF-8
            returnCode = 1;
            putchar(0xEF);
            putchar(0xBF);
            putchar(0xBD);
    
        } else if (c <= 0x7F) {
            // 1-byte UTF-8
            putchar(c);
    
        } else if (c <= 0x7FF) {
            // 2-byte UTF-8
            putchar(0xC0 | c>>6);
            putchar(0x80 | (c & 0x3F));
    
        } else if (c <= 0xFFFF) {
            // 3-byte UTF-8
            putchar(0xE0 | c>>12);
            putchar(0x80 | (c>>6 & 0x3F));
            putchar(0x80 | (c & 0x3F));
    
        } else {
            // 4-byte UTF-8
            putchar(0xF0 | c>>18);
            putchar(0x80 | (c>>12 & 0x3F));
            putchar(0x80 | (c>>6 & 0x3F));
            putchar(0x80 | (c & 0x3F));
        }
    }
    
    int main() {
        const int BUFFER_SIZE = 1024;
        uint32_t buffer[BUFFER_SIZE];
    
        while (true) {
            int codepointsRead = fread(buffer, 4, BUFFER_SIZE, stdin);
            if (codepointsRead == 0) break;
    
            for (int i = 0; i < codepointsRead; i++) {
                emit_utf8_for_codepoint(buffer[i]);
            }
        }
        return returnCode;
    }