Skip to content

Instantly share code, notes, and snippets.

@mmstick
Last active December 30, 2017 18:13
Show Gist options
  • Save mmstick/08c97c0ffc7a4131df3849a67f795dec to your computer and use it in GitHub Desktop.
Save mmstick/08c97c0ffc7a4131df3849a67f795dec to your computer and use it in GitHub Desktop.
Advent of Code 2017 (C Edition)
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <errno.h>
#include <string.h>
/// A fat pointer which points to byte array of text.
typedef struct str {
uint8_t *data;
size_t len;
} str;
/// An owned type for managing allocations and rellocations of an inner `str`.
typedef struct String {
str view;
size_t capacity;
} String;
/// Allocate a new `String` with a set capacity
String string_with_capacity(const size_t capacity) {
return (String) { (str) { malloc(capacity), 0 }, capacity };
}
/// Obtain a `str*` from the given `String`.
str *get_str(String *self) {
return &self->view;
}
/// Converts a byte array into a `str` without checking if it is UTF-8.
str from_utf8_unchecked(uint8_t *bytes, const size_t size) {
return (str) { bytes, size };
}
/// Mutably borrows the `String`, and appends the supplied string onto it.
/// If a reallocation is needed, a reallocation will occur.
void push_str(String *self, const str *append) {
if (self->capacity - self->view.len < append->len + 1) {
self->capacity *= 2;
while (self->capacity - self->view.len < append->len + 1) {
self->capacity *= 2;
}
self->view.data = realloc(self->view.data, self->capacity);
if (NULL == self->view.data) {
fprintf(stderr, "string reallocation failed\n");
exit(1);
}
}
memcpy(self->view.data + self->view.len, append->data, append->len);
self->view.len += append->len;
*(self->view.data + self->view.len) = '\0';
}
// Trims the input string
str trim(const str *input) {
size_t id = 0;
for (; id < input->len; id++) {
if (input->data[id] > 32) break;
}
str output = (str) { input->data + id, input->len - id };
for (id = 0; id < input->len; id++) {
if (input->data[input->len - 1 - id] > 32) break;
}
output.len -= id;
return output;
}
// NOTE: Result sum type implementations via tagged unions.
/// Effectively a `Result<String, ()>`
typedef struct TaggedString {
uint8_t _tag;
String string;
} TaggedString;
typedef struct IoError {
int errorno;
char *description;
} IoError;
/// Effectively a `Result<(), io::Error>`
typedef struct TaggedIoError {
uint8_t _tag;
IoError error;
} TaggedIoError;
/// Effectively a `Result<usize, ()>`
typedef struct TaggedUsize {
uint8_t _tag;
size_t value;
} TaggedUsize;
/// Emulates a `Result<String, io::Error>`
typedef union {
TaggedString ok;
TaggedIoError err;
} StringResult;
typedef union {
TaggedUsize ok;
TaggedIoError err;
} UsizeIoErrorResult;
/// A convenience method for `FILE` which simply reads the entire contents of
/// the file into a `String` supplied a buffer.
UsizeIoErrorResult read_to_end(FILE *file, String *buffer) {
uint8_t *temp_buffer[8192];
size_t total_read = 0;
while (1) {
const size_t read = fread(temp_buffer, 1, 8192, file);
if (read == 0) { break; }
total_read += read;
const str append = from_utf8_unchecked((uint8_t*) temp_buffer, read);
push_str(buffer, &append);
}
return (errno != 0)
? (UsizeIoErrorResult) (TaggedIoError) { 1, errno, "error reading input file" }
: (UsizeIoErrorResult) (TaggedUsize) { 0, total_read };
}
uint8_t is_numeric(uint8_t input) {
return input > 47 && input < 58;
}
/// Opens the supplied input file, reading it into a string.
StringResult get_input(char *path) {
FILE *file = fopen(path, "r");
String buffer = string_with_capacity(2048);
UsizeIoErrorResult result = read_to_end(file, &buffer);
return (result.ok._tag == 0)
? (StringResult) (TaggedString) { 0, buffer }
: (StringResult) result.err;
}
size_t get_sum_from(const str *input) {
uint8_t last_char = input->data[0] - 48;
size_t sum = 0;
for (size_t pos = 1; pos < input->len; pos++) {
uint8_t current = input->data[pos];
if (!is_numeric(current)) continue;
current -= 48;
if (last_char == current) sum += current;
last_char = current;
}
sum += (last_char == input->data[0] - 48) ? last_char : 0;
return sum;
}
size_t get_pos(const size_t length, const size_t current, size_t forward) {
forward = current + forward;
return (length <= forward)
? forward - length
: forward;
}
size_t get_sum_from2(const str *input) {
const size_t forward = input->len / 2;
size_t sum = 0;
for (size_t pos = 0; pos < input->len; pos++) {
uint8_t current = input->data[pos];
uint8_t future = input->data[get_pos(input->len, pos, forward)];
if (current == future && is_numeric(current) && is_numeric(future)) {
sum += current - 48;
}
}
uint8_t last_char = input->data[input->len-1] - 48;
sum += (last_char == input->data[forward] - 48) ? last_char : 0;
return sum;
}
typedef struct UnitTest {
uint8_t value;
uint8_t expected;
} UnitTest;
int tests() {
const size_t NTESTS = 6;
UnitTest tests[NTESTS];
tests[0] = (UnitTest) { get_sum_from(&(str) { (uint8_t*) "91212129", 8 }), 9 };
tests[1] = (UnitTest) { get_sum_from(&(str) { (uint8_t*) "1111", 4}), 4 };
tests[2] = (UnitTest) { get_sum_from2(&(str) { (uint8_t*) "12131415", 8 }), 4 };
tests[3] = (UnitTest) { get_sum_from2(&(str) { (uint8_t*) "123123", 6 }), 12 };
tests[4] = (UnitTest) { get_sum_from2(&(str) { (uint8_t*) "123425", 6 }), 4 };
tests[5] = (UnitTest) { get_sum_from2(&(str) { (uint8_t*) "1221", 4 }), 0 };
uint8_t failed = 0;
for (size_t test = 0; test < NTESTS; test++) {
UnitTest current_test = tests[test];
if (current_test.value != current_test.expected) {
fprintf(
stderr,
"test %lu failed: expected %u, found %u\n",
test + 1,
current_test.expected,
current_test.value
);
failed = 1;
} else {
fprintf(stderr, "test %lu passed\n", test + 1);
}
}
failed
? fprintf(stderr, "Some tests failed\n")
: fprintf(stderr, "All tests succeeded\n");
return failed;
}
int _main() {
StringResult result = get_input("../input");
if (1 == result.ok._tag) {
IoError err = result.err.error;
fprintf(stderr, "program error: %s: %s", strerror(err.errorno), err.description);
return 1;
}
const str view = trim(get_str(&result.ok.string));
const size_t sum1 = get_sum_from(&view);
printf("The result for the first part is %lu\n", sum1);
const size_t sum2 = get_sum_from2(&view);
printf("The result for the second part is %lu\n", sum2);
return 0;
}
int main() {
#ifdef DEBUG
return tests();
#else
return _main();
#endif
}
use std::fs::File;
use std::process::exit;
use std::io::{self, Read};
use std::path::Path;
fn get_input<P: AsRef<Path>>(path: P) -> io::Result<String> {
let mut file = File::open(path)?;
let mut buffer = String::with_capacity(2048);
file.read_to_string(&mut buffer)?;
Ok(buffer)
}
fn is_numeric(byte: u8) -> bool {
byte > 47 && byte < 58
}
fn get_sum_from(input: &str) -> usize {
let input = input.as_bytes();
let mut last_char = input[0] - 48;
let mut sum: usize = 0;
for pos in 1..input.len() {
let mut current = input[pos];
if !is_numeric(current) { continue }
current -= 48;
sum += if last_char == current { current as usize } else { 0 };
last_char = current;
}
if last_char == input[0] - 48 { sum + last_char as usize } else { sum }
}
fn get_pos(length: usize, current: usize, forward: usize) -> usize {
let forward = current + forward;
if length <= forward { forward - length } else { forward }
}
fn get_sum_from2(input: &str) -> usize {
let input = input.as_bytes();
let forward = input.len() / 2;
let mut sum = 0;
for pos in 0..input.len() {
let mut current = input[pos];
let mut future = input[get_pos(input.len(), pos, forward)];
if current == future && is_numeric(current) && is_numeric(future) {
sum += (current - 48) as usize;
}
}
sum
}
fn main() {
let input: String = match get_input("../input") {
Ok(input) => input,
Err(why) => {
eprintln!("program error: {}", why);
exit(1);
}
};
let input = input.trim();
let sum1 = get_sum_from(input);
let sum2 = get_sum_from2(input);
println!("The result for the first part is {}", sum1);
println!("The result for the second part is {}", sum2);
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn first_part() {
assert_eq!(9, get_sum_from("91212129"));
assert_eq!(4, get_sum_from("1111"));
}
#[test]
fn second_part() {
assert_eq!(4, get_sum_from2("12131415"));
assert_eq!(12, get_sum_from2("123123"));
assert_eq!(4, get_sum_from2("123425"));
assert_eq!(0, get_sum_from2("1221"));
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment