-
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;
}
-
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;
}
-
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;
}
-
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;
}
-
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;
}