summary refs log tree commit diff homepage
path: root/src
diff options
context:
space:
mode:
authorJune McEnroe <june@causal.agency>2017-11-27 17:11:18 -0500
committerJune McEnroe <june@causal.agency>2020-11-22 00:14:25 -0500
commit051be932a389b8bc3ea5d4626575454844639066 (patch)
tree9383502b3205624f7aee8faa014228036b5450f9 /src
parentLicense ISC (diff)
downloadaoc-051be932a389b8bc3ea5d4626575454844639066.tar.gz
aoc-051be932a389b8bc3ea5d4626575454844639066.zip
Move to 2016 directory
Diffstat (limited to 'src')
-rw-r--r--src/bin/day01.rs78
-rw-r--r--src/bin/day02.rs156
-rw-r--r--src/bin/day03.rs62
-rw-r--r--src/bin/day04.rs123
-rw-r--r--src/bin/day05.rs71
-rw-r--r--src/bin/day06.rs87
-rw-r--r--src/bin/day07.rs119
-rw-r--r--src/bin/day08.rs148
-rw-r--r--src/bin/day09.rs124
-rw-r--r--src/bin/day10.rs180
-rw-r--r--src/bin/day11.rs194
-rw-r--r--src/bin/day12.rs143
-rw-r--r--src/bin/day13.rs122
-rw-r--r--src/bin/day14.rs87
-rw-r--r--src/bin/day15.rs103
-rw-r--r--src/bin/day16.rs51
-rw-r--r--src/bin/day17.rs148
-rw-r--r--src/bin/day18.rs88
-rw-r--r--src/bin/day19.rs35
-rw-r--r--src/bin/day20.rs77
-rw-r--r--src/bin/day21.rs144
-rw-r--r--src/bin/day23.rs157
-rw-r--r--src/bin/day25.rs163
23 files changed, 0 insertions, 2660 deletions
diff --git a/src/bin/day01.rs b/src/bin/day01.rs
deleted file mode 100644
index c596099..0000000
--- a/src/bin/day01.rs
+++ /dev/null
@@ -1,78 +0,0 @@
-use std::collections::HashSet;
-use std::io::{self, Read};
-use std::ops::Add;
-
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-struct Point(i32, i32);
-
-impl Point {
-    fn left(self) -> Self {
-        Point(self.1, -self.0)
-    }
-
-    fn right(self) -> Self {
-        Point(-self.1, self.0)
-    }
-
-    fn distance(self) -> i32 {
-        self.0.abs() + self.1.abs()
-    }
-}
-
-impl Add for Point {
-    type Output = Self;
-
-    fn add(self, rhs: Self) -> Self {
-        Point(self.0 + rhs.0, self.1 + rhs.1)
-    }
-}
-
-fn solve(input: &str) -> (i32, Option<i32>) {
-    let mut position = Point(0, 0);
-    let mut direction = Point(0, -1);
-    let mut visited = HashSet::new();
-    let mut collision = None;
-
-    for instruction in input.trim().split(", ") {
-        let (turn, count) = instruction.split_at(1);
-
-        if turn == "L" {
-            direction = direction.left();
-        } else {
-            direction = direction.right();
-        }
-
-        let count: i32 = count.parse().unwrap();
-        for _ in 0..count {
-            position = position + direction;
-            if collision.is_none() && !visited.insert(position) {
-                collision = Some(position);
-            }
-        }
-    }
-
-    (position.distance(), collision.map(Point::distance))
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    let (part1, part2) = solve(&input);
-    println!("Part 1: {}", part1);
-    if let Some(part2) = part2 {
-        println!("Part 2: {}", part2);
-    }
-}
-
-#[test]
-fn part1() {
-    assert_eq!(5, solve("R2, L3").0);
-    assert_eq!(2, solve("R2, R2, R2").0);
-    assert_eq!(12, solve("R5, L5, R5, R3").0);
-}
-
-#[test]
-fn part2() {
-    assert_eq!(Some(4), solve("R8, R4, R4, R8").1);
-}
diff --git a/src/bin/day02.rs b/src/bin/day02.rs
deleted file mode 100644
index f7cc624..0000000
--- a/src/bin/day02.rs
+++ /dev/null
@@ -1,156 +0,0 @@
-use std::io::{self, Read};
-
-trait Pad: Copy + Default {
-    fn up(self) -> Self;
-    fn down(self) -> Self;
-    fn left(self) -> Self;
-    fn right(self) -> Self;
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum Keypad {
-    K1, K2, K3,
-    K4, K5, K6,
-    K7, K8, K9,
-}
-
-impl Default for Keypad {
-    fn default() -> Self { Keypad::K5 }
-}
-
-impl Pad for Keypad {
-    fn up(self) -> Self {
-        use Keypad::*;
-        match self {
-            K4 => K1, K5 => K2, K6 => K3,
-            K7 => K4, K8 => K5, K9 => K6,
-            _ => self,
-        }
-    }
-
-    fn down(self) -> Self {
-        use Keypad::*;
-        match self {
-            K1 => K4, K2 => K5, K3 => K6,
-            K4 => K7, K5 => K8, K6 => K9,
-            _ => self,
-        }
-    }
-
-    fn left(self) -> Self {
-        use Keypad::*;
-        match self {
-            K2 => K1, K3 => K2,
-            K5 => K4, K6 => K5,
-            K8 => K7, K9 => K8,
-            _ => self,
-        }
-    }
-
-    fn right(self) -> Self {
-        use Keypad::*;
-        match self {
-            K1 => K2, K2 => K3,
-            K4 => K5, K5 => K6,
-            K7 => K8, K8 => K9,
-            _ => self,
-        }
-    }
-}
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq)]
-enum Wackypad {
-            K1,
-        K2, K3, K4,
-    K5, K6, K7, K8, K9,
-        KA, KB, KC,
-            KD,
-}
-
-impl Default for Wackypad {
-    fn default() -> Self { Wackypad::K5 }
-}
-
-impl Pad for Wackypad {
-    fn up(self) -> Self {
-        use Wackypad::*;
-        match self {
-                      K3 => K1,
-            K6 => K2, K7 => K3, K8 => K4,
-            KA => K6, KB => K7, KC => K8,
-                      KD => KB,
-            _ => self,
-        }
-    }
-
-    fn down(self) -> Self {
-        use Wackypad::*;
-        match self {
-                      K1 => K3,
-            K2 => K6, K3 => K7, K4 => K8,
-            K6 => KA, K7 => KB, K8 => KC,
-                      KB => KD,
-            _ => self,
-        }
-    }
-
-    fn left(self) -> Self {
-        use Wackypad::*;
-        match self {
-                      K3 => K2, K4 => K3,
-            K6 => K5, K7 => K6, K8 => K7, K9 => K8,
-                      KB => KA, KC => KB,
-            _ => self,
-        }
-    }
-
-    fn right(self) -> Self {
-        use Wackypad::*;
-        match self {
-                      K2 => K3, K3 => K4,
-            K5 => K6, K6 => K7, K7 => K8, K8 => K9,
-                      KA => KB, KB => KC,
-            _ => self,
-        }
-    }
-}
-
-fn solve<P: Pad>(input: &str) -> Vec<P> {
-    let mut code = Vec::new();
-    let mut key = P::default();
-
-    for line in input.lines() {
-        for direction in line.chars() {
-            key = match direction {
-                'U' => key.up(),
-                'D' => key.down(),
-                'L' => key.left(),
-                'R' => key.right(),
-                _ => panic!("{} is not a direction", direction),
-            }
-        }
-        code.push(key);
-    }
-
-    code
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {:?}", solve::<Keypad>(&input));
-    println!("Part 2: {:?}", solve::<Wackypad>(&input));
-}
-
-#[test]
-fn part1() {
-    use Keypad::*;
-    assert_eq!(vec![K1, K9, K8, K5], solve("ULL\nRRDDD\nLURDL\nUUUUD"));
-}
-
-#[test]
-fn part2() {
-    use Wackypad::*;
-    assert_eq!(vec![K5, KD, KB, K3], solve("ULL\nRRDDD\nLURDL\nUUUUD"));
-}
diff --git a/src/bin/day03.rs b/src/bin/day03.rs
deleted file mode 100644
index 76b5aa5..0000000
--- a/src/bin/day03.rs
+++ /dev/null
@@ -1,62 +0,0 @@
-use std::io::{self, Read};
-use std::str::FromStr;
-
-struct Triangle(u32, u32, u32);
-
-impl Triangle {
-    fn valid(&self) -> bool {
-        self.0 + self.1 > self.2
-            && self.1 + self.2 > self.0
-            && self.0 + self.2 > self.1
-    }
-}
-
-impl FromStr for Triangle {
-    type Err = ();
-    fn from_str(s: &str) -> Result<Self, ()> {
-        let mut iter = s.split_whitespace().map(str::parse);
-        match (iter.next(), iter.next(), iter.next()) {
-            (Some(Ok(a)), Some(Ok(b)), Some(Ok(c))) => Ok(Triangle(a, b, c)),
-            _ => Err(()),
-        }
-    }
-}
-
-fn solve1(input: &str) -> usize {
-    input.lines()
-        .map(str::parse)
-        .map(Result::unwrap)
-        .filter(Triangle::valid)
-        .count()
-}
-
-fn solve2(input: &str) -> usize {
-    let triangles: Vec<Triangle> = input.lines()
-        .map(str::parse)
-        .map(Result::unwrap)
-        .collect();
-
-    triangles.chunks(3)
-        .flat_map(|triple| {
-            vec![
-                Triangle(triple[0].0, triple[1].0, triple[2].0),
-                Triangle(triple[0].1, triple[1].1, triple[2].1),
-                Triangle(triple[0].2, triple[1].2, triple[2].2),
-            ]
-        })
-        .filter(Triangle::valid)
-        .count()
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve1(&input));
-    println!("Part 2: {}", solve2(&input));
-}
-
-#[test]
-fn part1() {
-    assert_eq!(0, solve1("5 10 25"));
-}
diff --git a/src/bin/day04.rs b/src/bin/day04.rs
deleted file mode 100644
index 8e724c2..0000000
--- a/src/bin/day04.rs
+++ /dev/null
@@ -1,123 +0,0 @@
-use std::io::{self, Read};
-use std::str::FromStr;
-
-struct Room {
-    name: String,
-    sector_id: u32,
-    checksum: String,
-}
-
-impl FromStr for Room {
-    type Err = ();
-    fn from_str(s: &str) -> Result<Self, ()> {
-        let mut iter = s.trim_right_matches(']')
-            .rsplitn(3, |c| c == '-' || c == '[');
-
-        let checksum = iter.next().ok_or(())?;
-        let sector_id = iter.next().ok_or(())?;
-        let name = iter.next().ok_or(())?;
-
-        Ok(Room {
-            name: name.into(),
-            sector_id: sector_id.parse().map_err(|_| ())?,
-            checksum: checksum.into(),
-        })
-    }
-}
-
-impl Room {
-    fn checksum(&self) -> String {
-        let mut letters = [
-            (0, 'a'), (0, 'b'), (0, 'c'), (0, 'd'), (0, 'e'), (0, 'f'), (0, 'g'), (0, 'h'),
-            (0, 'i'), (0, 'j'), (0, 'k'), (0, 'l'), (0, 'm'), (0, 'n'), (0, 'o'), (0, 'p'),
-            (0, 'q'), (0, 'r'), (0, 's'), (0, 't'), (0, 'u'), (0, 'v'), (0, 'w'), (0, 'x'),
-            (0, 'y'), (0, 'z'),
-        ];
-
-        for letter in self.name.chars().filter(|c| c.is_alphabetic()) {
-            let index = letter as u8 - b'a';
-            letters[index as usize].0 -= 1;
-        }
-
-        letters.sort();
-
-        letters.into_iter()
-            .map(|l| l.1)
-            .take(5)
-            .collect()
-    }
-
-    fn verify_checksum(&self) -> bool {
-        self.checksum == self.checksum()
-    }
-
-    fn decrypt_name(&self) -> String {
-        self.name.chars()
-            .map(|c| {
-                match c {
-                    '-' => ' ',
-                    _ => rotate(c, self.sector_id),
-                }
-            })
-            .collect()
-    }
-}
-
-fn rotate(c: char, n: u32) -> char {
-    let c = c as u8 + (n % 26) as u8;
-    if c > b'z' {
-        (c - 26) as char
-    } else {
-        c as char
-    }
-}
-
-fn solve1(input: &str) -> u32 {
-    input.lines()
-        .map(str::parse)
-        .map(Result::unwrap)
-        .filter(Room::verify_checksum)
-        .map(|room| room.sector_id)
-        .sum()
-}
-
-fn solve2(input: &str) -> Option<u32> {
-    input.lines()
-        .map(str::parse)
-        .map(Result::unwrap)
-        .filter(Room::verify_checksum)
-        .filter_map(|r| {
-            match r.decrypt_name().as_str() {
-                "northpole object storage" => Some(r.sector_id),
-                _ => None,
-            }
-        })
-        .next()
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve1(&input));
-    if let Some(sector_id) = solve2(&input) {
-        println!("Part 2: {}", sector_id);
-    }
-}
-
-#[test]
-fn part1() {
-    let input = "
-aaaaa-bbb-z-y-x-123[abxyz]
-a-b-c-d-e-f-g-h-987[abcde]
-not-a-real-room-404[oarel]
-totally-real-room-200[decoy]
-";
-    assert_eq!(1514, solve1(&input[1..]));
-}
-
-#[test]
-fn part2() {
-    let room: Room = "qzmt-zixmtkozy-ivhz-343[zimth]".parse().unwrap();
-    assert_eq!("very encrypted name", room.decrypt_name());
-}
diff --git a/src/bin/day05.rs b/src/bin/day05.rs
deleted file mode 100644
index eefa3df..0000000
--- a/src/bin/day05.rs
+++ /dev/null
@@ -1,71 +0,0 @@
-extern crate crypto;
-
-use std::io::{self, Read};
-
-use crypto::digest::Digest;
-use crypto::md5::Md5;
-
-fn solve1(input: &str) -> String {
-    let mut password = String::new();
-    let mut index = 0u64;
-
-    while password.len() < 8 {
-        let mut md5 = Md5::new();
-        md5.input_str(input);
-        md5.input_str(&index.to_string());
-        let digest = md5.result_str();
-
-        if &digest[0..5] == "00000" {
-            password.push_str(&digest[5..6]);
-        }
-
-        index += 1;
-    }
-
-    password
-}
-
-fn solve2(input: &str) -> String {
-    let mut password = [None; 8];
-    let mut index = 0u64;
-
-    while !password.iter().all(Option::is_some) {
-        let mut md5 = Md5::new();
-        md5.input_str(input);
-        md5.input_str(&index.to_string());
-        let digest = md5.result_str();
-
-        if &digest[0..5] == "00000" {
-            if let Some(pos @ 0 ... 7) = digest[5..6].parse().ok() {
-                password[pos] = password[pos].or(digest[6..7].chars().next());
-            }
-        }
-
-        index += 1;
-    }
-
-    password.iter()
-        .cloned()
-        .map(Option::unwrap)
-        .collect()
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve1(input.trim()));
-    println!("Part 2: {}", solve2(input.trim()));
-}
-
-#[test]
-#[ignore]
-fn part1() {
-    assert_eq!("18f47a30", solve1("abc"));
-}
-
-#[test]
-#[ignore]
-fn part2() {
-    assert_eq!("05ace8e3", solve2("abc"));
-}
diff --git a/src/bin/day06.rs b/src/bin/day06.rs
deleted file mode 100644
index 7f8a516..0000000
--- a/src/bin/day06.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-use std::collections::HashMap;
-use std::io::{self, Read};
-
-fn frequencies(chars: &[char]) -> HashMap<char, u32> {
-    let mut map = HashMap::new();
-    for &ch in chars {
-        *map.entry(ch).or_insert(0) += 1;
-    }
-    map
-}
-
-fn solve1(input: &str) -> String {
-    let len = input.find('\n').unwrap_or(input.len());
-    let mut columns = vec![Vec::new(); len];
-
-    for line in input.lines() {
-        for (i, ch) in line.chars().enumerate() {
-            columns[i].push(ch);
-        }
-    }
-
-    columns.into_iter()
-        .map(|column| frequencies(&column))
-        .map(IntoIterator::into_iter)
-        .map(|iter| iter.max_by_key(|&(_, v)| v))
-        .map(Option::unwrap)
-        .map(|(ch, _)| ch)
-        .collect()
-}
-
-fn solve2(input: &str) -> String {
-    let len = input.find('\n').unwrap_or(input.len());
-    let mut columns = vec![Vec::new(); len];
-
-    for line in input.lines() {
-        for (i, ch) in line.chars().enumerate() {
-            columns[i].push(ch);
-        }
-    }
-
-    columns.into_iter()
-        .map(|column| frequencies(&column))
-        .map(IntoIterator::into_iter)
-        .map(|iter| iter.min_by_key(|&(_, v)| v))
-        .map(Option::unwrap)
-        .map(|(ch, _)| ch)
-        .collect()
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve1(&input));
-    println!("Part 2: {}", solve2(&input));
-}
-
-#[cfg(test)]
-const TEST_INPUT: &'static str = "
-eedadn
-drvtee
-eandsr
-raavrd
-atevrs
-tsrnev
-sdttsa
-rasrtv
-nssdts
-ntnada
-svetve
-tesnvt
-vntsnd
-vrdear
-dvrsen
-enarar
-";
-
-
-#[test]
-fn part1() {
-    assert_eq!("easter", solve1(TEST_INPUT.trim()));
-}
-
-#[test]
-fn part2() {
-    assert_eq!("advent", solve2(TEST_INPUT.trim()));
-}
diff --git a/src/bin/day07.rs b/src/bin/day07.rs
deleted file mode 100644
index b4967ff..0000000
--- a/src/bin/day07.rs
+++ /dev/null
@@ -1,119 +0,0 @@
-use std::io::{self, Read};
-use std::str::FromStr;
-
-fn has_abba(s: &str) -> bool {
-    s.as_bytes()
-        .windows(4)
-        .any(|window| {
-            window[0] == window[3]
-                && window[1] == window[2]
-                && window[0] != window[1]
-        })
-}
-
-fn abas(s: &str) -> Vec<&[u8]> {
-    s.as_bytes()
-        .windows(3)
-        .filter(|window| {
-            window[0] == window[2]
-                && window[0] != window[1]
-        })
-        .collect()
-}
-
-fn has_bab(s: &str, aba: &[u8]) -> bool {
-    s.as_bytes()
-        .windows(3)
-        .any(|window| {
-            window[0] == aba[1]
-                && window[1] == aba[0]
-                && window[2] == aba[1]
-        })
-}
-
-#[derive(Default)]
-struct Ip {
-    supernet: Vec<String>,
-    hypernet: Vec<String>,
-}
-
-impl Ip {
-    fn supports_tls(&self) -> bool {
-        self.supernet.iter().any(|s| has_abba(s))
-            && !self.hypernet.iter().any(|s| has_abba(s))
-    }
-
-    fn supports_ssl(&self) -> bool {
-        self.supernet
-            .iter()
-            .flat_map(|s| abas(s))
-            .any(|aba| {
-                self.hypernet
-                    .iter()
-                    .any(|s| has_bab(s, aba))
-            })
-    }
-}
-
-impl FromStr for Ip {
-    type Err = ();
-    fn from_str(s: &str) -> Result<Ip, ()> {
-        let mut ip = Ip::default();
-
-        for (i, seq) in s.split(|ch| ch == '[' || ch == ']').enumerate() {
-            if i % 2 == 0 {
-                ip.supernet.push(seq.to_owned());
-            } else {
-                ip.hypernet.push(seq.to_owned());
-            }
-        }
-
-        Ok(ip)
-    }
-}
-
-fn solve1(input: &str) -> usize {
-    input.lines()
-        .map(str::parse)
-        .map(Result::unwrap)
-        .filter(Ip::supports_tls)
-        .count()
-}
-
-fn solve2(input: &str) -> usize {
-    input.lines()
-        .map(str::parse)
-        .map(Result::unwrap)
-        .filter(Ip::supports_ssl)
-        .count()
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve1(&input));
-    println!("Part 2: {}", solve2(&input));
-}
-
-#[test]
-fn part1() {
-    let input = "
-abba[mnop]qrst
-abcd[bddb]xyyx
-aaaa[qwer]tyui
-ioxxoj[asdfgh]zxcvbn
-";
-    assert_eq!(2, solve1(input.trim()));
-}
-
-#[test]
-fn part2() {
-    let input = "
-aba[bab]xyz
-xyx[xyx]xyx
-aaa[kek]eke
-zazbz[bzb]cdb
-";
-    assert_eq!(3, solve2(input.trim()));
-}
diff --git a/src/bin/day08.rs b/src/bin/day08.rs
deleted file mode 100644
index 1443ec3..0000000
--- a/src/bin/day08.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-use std::fmt::{Display as FmtDisplay, Error as FmtError, Formatter};
-use std::io::{self, Read};
-use std::ops::{Index, IndexMut};
-use std::str::FromStr;
-
-enum Operation {
-    Rect(usize, usize),
-    RotateRow(usize, usize),
-    RotateColumn(usize, usize),
-}
-
-impl FromStr for Operation {
-    type Err = ();
-    fn from_str(s: &str) -> Result<Self, ()> {
-        if s.starts_with("rect") {
-            let mut iter = s[5..].split('x');
-            let width = iter.next().ok_or(())?.parse().map_err(|_| ())?;
-            let height = iter.next().ok_or(())?.parse().map_err(|_| ())?;
-            Ok(Operation::Rect(width, height))
-
-        } else if s.starts_with("rotate row") {
-            let mut iter = s[13..].split(" by ");
-            let y = iter.next().ok_or(())?.parse().map_err(|_| ())?;
-            let count = iter.next().ok_or(())?.parse().map_err(|_| ())?;
-            Ok(Operation::RotateRow(y, count))
-
-        } else if s.starts_with("rotate column") {
-            let mut iter = s[16..].split(" by ");
-            let x = iter.next().ok_or(())?.parse().map_err(|_| ())?;
-            let count = iter.next().ok_or(())?.parse().map_err(|_| ())?;
-            Ok(Operation::RotateColumn(x, count))
-
-        } else {
-            Err(())
-        }
-    }
-}
-
-struct Display {
-    width: usize,
-    height: usize,
-    pixels: Box<[bool]>,
-}
-
-impl Index<(usize, usize)> for Display {
-    type Output = bool;
-    fn index(&self, index: (usize, usize)) -> &bool {
-        &self.pixels[index.1 * self.width + index.0]
-    }
-}
-
-impl IndexMut<(usize, usize)> for Display {
-    fn index_mut(&mut self, index: (usize, usize)) -> &mut bool {
-        &mut self.pixels[index.1 * self.width + index.0]
-    }
-}
-
-impl Display {
-    fn new(width: usize, height: usize) -> Self {
-        Display {
-            width: width,
-            height: height,
-            pixels: vec![false; width * height].into_boxed_slice(),
-        }
-    }
-
-    fn apply(&mut self, operation: Operation) {
-        match operation {
-            Operation::Rect(width, height) => {
-                for y in 0..height {
-                    for x in 0..width {
-                        self[(x, y)] = true;
-                    }
-                }
-            },
-
-            Operation::RotateRow(y, count) => {
-                for _ in 0..count {
-                    let last = self[(self.width - 1, y)];
-                    for x in (1..self.width).rev() {
-                        self[(x, y)] = self[(x - 1, y)];
-                    }
-                    self[(0, y)] = last;
-                }
-            },
-
-            Operation::RotateColumn(x, count) => {
-                for _ in 0..count {
-                    let last = self[(x, self.height - 1)];
-                    for y in (1..self.height).rev() {
-                        self[(x, y)] = self[(x, y - 1)];
-                    }
-                    self[(x, 0)] = last;
-                }
-            },
-        }
-    }
-
-    fn pixels_lit(&self) -> usize {
-        self.pixels.iter().filter(|&&p| p).count()
-    }
-}
-
-impl FmtDisplay for Display {
-    fn fmt(&self, f: &mut Formatter) -> Result<(), FmtError> {
-        for y in 0..self.height {
-            for x in 0..self.width {
-                if self[(x, y)] {
-                    write!(f, "#")?;
-                } else {
-                    write!(f, " ")?;
-                }
-            }
-            write!(f, "\n")?;
-        }
-        Ok(())
-    }
-}
-
-fn solve(width: usize, height: usize, input: &str) -> Display {
-    let mut display = Display::new(width, height);
-
-    for line in input.lines() {
-        display.apply(line.parse().unwrap());
-    }
-
-    display
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    let display = solve(50, 6, &input);
-    println!("Part 1: {}", display.pixels_lit());
-    println!("Part 2:\n{}", display);
-}
-
-#[test]
-fn part1() {
-    let input = "
-rect 3x2
-rotate column x=1 by 1
-rotate row y=0 by 4
-rotate row x=1 by 1
-";
-    assert_eq!(6, solve(7, 3, input.trim()).pixels_lit());
-}
diff --git a/src/bin/day09.rs b/src/bin/day09.rs
deleted file mode 100644
index f559b47..0000000
--- a/src/bin/day09.rs
+++ /dev/null
@@ -1,124 +0,0 @@
-use std::io::{self, Read};
-
-enum Chunk<'a> {
-    Literal(&'a str),
-    Repeat(u32, &'a str),
-    Recursive(u32, Vec<Chunk<'a>>),
-}
-
-impl<'a> Chunk<'a> {
-    fn len(&self) -> usize {
-        match *self {
-            Chunk::Literal(s) => s.len(),
-            Chunk::Repeat(n, s) => n as usize * s.len(),
-            Chunk::Recursive(n, ref cs) => n as usize * cs.iter().map(Chunk::len).sum::<usize>(),
-        }
-    }
-
-    fn parse(s: &'a str) -> Result<(Self, &'a str), ()> {
-        if s.starts_with('(') {
-            let mut iter = s[1..].splitn(3, |ch| ch == 'x' || ch == ')');
-
-            let len = iter.next().ok_or(())?.parse().map_err(|_| ())?;
-            let repeat = iter.next().ok_or(())?.parse().map_err(|_| ())?;
-            let (data, rest) = iter.next().ok_or(())?.split_at(len);
-
-            Ok((Chunk::Repeat(repeat, data), rest))
-        } else {
-            let paren = s.find('(').unwrap_or(s.len());
-            Ok((Chunk::Literal(&s[..paren]), &s[paren..]))
-        }
-    }
-
-    fn parse_v2(s: &'a str) -> Result<(Self, &'a str), ()> {
-        if s.starts_with('(') {
-            let mut iter = s[1..].splitn(3, |ch| ch == 'x' || ch == ')');
-
-            let len = iter.next().ok_or(())?.parse().map_err(|_| ())?;
-            let repeat = iter.next().ok_or(())?.parse().map_err(|_| ())?;
-            let (mut data, rest) = iter.next().ok_or(())?.split_at(len);
-
-            let mut chunks = Vec::new();
-            while !data.is_empty() {
-                let (chunk, data_rest) = Chunk::parse_v2(data)?;
-                chunks.push(chunk);
-                data = data_rest;
-            }
-
-            Ok((Chunk::Recursive(repeat, chunks), rest))
-        } else {
-            let paren = s.find('(').unwrap_or(s.len());
-            Ok((Chunk::Literal(&s[..paren]), &s[paren..]))
-        }
-    }
-}
-
-struct File<'a> {
-    chunks: Vec<Chunk<'a>>,
-}
-
-impl<'a> File<'a> {
-    fn len(&self) -> usize {
-        self.chunks.iter().map(Chunk::len).sum()
-    }
-
-    fn parse(mut s: &'a str) -> Result<Self, ()> {
-        let mut chunks = Vec::new();
-
-        while !s.is_empty() {
-            let (chunk, rest) = Chunk::parse(s)?;
-            chunks.push(chunk);
-            s = rest;
-        }
-
-        Ok(File { chunks: chunks })
-    }
-
-    fn parse_v2(mut s: &'a str) -> Result<Self, ()> {
-        let mut chunks = Vec::new();
-
-        while !s.is_empty() {
-            let (chunk, rest) = Chunk::parse_v2(s)?;
-            chunks.push(chunk);
-            s = rest;
-        }
-
-        Ok(File { chunks: chunks })
-    }
-}
-
-fn solve1(input: &str) -> usize {
-    let file = File::parse(input).unwrap();
-    file.len()
-}
-
-fn solve2(input: &str) -> usize {
-    let file = File::parse_v2(input).unwrap();
-    file.len()
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve1(&input));
-    println!("Part 2: {}", solve2(&input));
-}
-
-#[test]
-fn part1() {
-    assert_eq!(6, solve1("ADVENT"));
-    assert_eq!(7, solve1("A(1x5)BC"));
-    assert_eq!(9, solve1("(3x3)XYZ"));
-    assert_eq!(11, solve1("A(2x2)BCD(2x2)EFG"));
-    assert_eq!(6, solve1("(6x1)(1x3)A"));
-    assert_eq!(18, solve1("X(8x2)(3x3)ABCY"));
-}
-
-#[test]
-fn part2() {
-    assert_eq!(9, solve2("(3x3)XYZ"));
-    assert_eq!(20, solve2("X(8x2)(3x3)ABCY"));
-    assert_eq!(241920, solve2("(27x12)(20x12)(13x14)(7x10)(1x12)A"));
-    assert_eq!(445, solve2("(25x3)(3x3)ABC(2x3)XY(5x2)PQRSTX(18x9)(3x2)TWO(5x7)SEVEN"));
-}
diff --git a/src/bin/day10.rs b/src/bin/day10.rs
deleted file mode 100644
index a824f56..0000000
--- a/src/bin/day10.rs
+++ /dev/null
@@ -1,180 +0,0 @@
-use std::collections::HashMap;
-use std::io::{self, Read};
-use std::str::FromStr;
-
-#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
-struct Chip(u32);
-
-#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
-struct Bot(u32);
-
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-struct Output(u32);
-
-#[derive(Clone, Copy, Default)]
-struct BotChips(Option<Chip>, Option<Chip>);
-
-impl BotChips {
-    fn add(&mut self, chip: Chip) {
-        match (self.0, self.1) {
-            (None, None) => {
-                self.0 = Some(chip);
-            },
-            (Some(low), None) if low < chip => {
-                self.1 = Some(chip);
-            },
-            (Some(high), None) => {
-                self.0 = Some(chip);
-                self.1 = Some(high);
-            },
-            _ => panic!("bot has too many chips"),
-        }
-    }
-
-    fn has_two(&self) -> bool {
-        self.0.is_some() && self.1.is_some()
-    }
-}
-
-#[derive(Clone, Copy)]
-enum Destination {
-    Bot(Bot),
-    Output(Output),
-}
-
-impl Destination {
-    fn from_pair(ty: &str, value: u32) -> Result<Self, ()> {
-        match ty {
-            "bot" => Ok(Destination::Bot(Bot(value))),
-            "output" => Ok(Destination::Output(Output(value))),
-            _ => Err(()),
-        }
-    }
-}
-
-#[derive(Default)]
-struct Instructions {
-    chips: HashMap<Chip, Bot>,
-    bots: HashMap<Bot, (Destination, Destination)>,
-}
-
-impl FromStr for Instructions {
-    type Err = ();
-    fn from_str(s: &str) -> Result<Self, ()> {
-        let mut instructions = Instructions::default();
-
-        for line in s.lines() {
-            let mut words = line.split(' ');
-            if words.next() == Some("value") {
-                let value = words.next().ok_or(())?.parse().map_err(|_| ())?;
-                let bot = words.nth(3).ok_or(())?.parse().map_err(|_| ())?;
-
-                instructions.chips.insert(Chip(value), Bot(bot));
-            } else {
-                let bot = words.next().ok_or(())?.parse().map_err(|_| ())?;
-                let low_type = words.nth(3).ok_or(())?;
-                let low_value = words.next().ok_or(())?.parse().map_err(|_| ())?;
-                let high_type = words.nth(3).ok_or(())?;
-                let high_value = words.next().ok_or(())?.parse().map_err(|_| ())?;
-
-                let low = Destination::from_pair(low_type, low_value)?;
-                let high = Destination::from_pair(high_type, high_value)?;
-
-                instructions.bots.insert(Bot(bot), (low, high));
-            }
-        }
-
-        Ok(instructions)
-    }
-}
-
-#[derive(Default)]
-struct State {
-    outputs: HashMap<Output, Chip>,
-    bots: HashMap<Bot, BotChips>,
-    comparisons: HashMap<(Chip, Chip), Bot>,
-}
-
-impl State {
-    fn initialize(&mut self, instructions: &Instructions) {
-        for (&chip, &bot) in &instructions.chips {
-            self.bots.entry(bot).or_insert_with(Default::default).add(chip);
-        }
-    }
-
-    fn step(&mut self, instructions: &Instructions) -> bool {
-        let active_bots: Vec<Bot> = self.bots.iter()
-            .filter(|&(_, ref chips)| chips.has_two())
-            .map(|(&bot, _)| bot)
-            .collect();
-
-        if active_bots.is_empty() {
-            return false;
-        }
-
-        for bot in active_bots {
-            let (low, high) = {
-                let chips = self.bots.get_mut(&bot).unwrap();
-                (chips.0.take().unwrap(), chips.1.take().unwrap())
-            };
-            self.comparisons.insert((low, high), bot);
-
-            let &(low_dest, high_dest) = instructions.bots.get(&bot).unwrap();
-            self.give_to(low_dest, low);
-            self.give_to(high_dest, high);
-        }
-
-        true
-    }
-
-    fn give_to(&mut self, destination: Destination, chip: Chip) {
-        match destination {
-            Destination::Bot(bot) => {
-                self.bots.entry(bot).or_insert_with(Default::default).add(chip);
-            },
-            Destination::Output(output) => {
-                self.outputs.insert(output, chip);
-            },
-        }
-    }
-}
-
-fn solve1(comparison: (Chip, Chip), input: &str) -> Option<Bot> {
-    let instructions = input.parse().unwrap();
-    let mut state = State::default();
-    state.initialize(&instructions);
-    while state.step(&instructions) { }
-    state.comparisons.get(&comparison).cloned()
-}
-
-fn solve2(input: &str) -> u32 {
-    let instructions = input.parse().unwrap();
-    let mut state = State::default();
-    state.initialize(&instructions);
-    while state.step(&instructions) { }
-
-    state.outputs.get(&Output(0)).unwrap().0
-        * state.outputs.get(&Output(1)).unwrap().0
-        * state.outputs.get(&Output(2)).unwrap().0
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {:?}", solve1((Chip(17), Chip(61)), &input));
-    println!("Part 2: {}", solve2(&input));
-}
-
-#[test]
-fn part1() {
-    let input = "
-value 5 goes to bot 2
-bot 2 gives low to bot 1 and high to bot 0
-value 3 goes to bot 1
-bot 1 gives low to output 1 and high to bot 0
-bot 0 gives low to output 2 and high to output 0
-value 2 goes to bot 2
-";
-    assert_eq!(Some(Bot(2)), solve1((Chip(2), Chip(5)), input.trim()));
-}
diff --git a/src/bin/day11.rs b/src/bin/day11.rs
deleted file mode 100644
index c033766..0000000
--- a/src/bin/day11.rs
+++ /dev/null
@@ -1,194 +0,0 @@
-use std::collections::{HashSet, VecDeque};
-
-#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
-enum Object {
-    Microchip(u8),
-    Generator(u8),
-}
-
-impl Object {
-    fn is_microchip(self) -> bool {
-        match self {
-            Object::Microchip(_) => true,
-            _ => false,
-        }
-    }
-
-    fn is_generator(self) -> bool {
-        match self {
-            Object::Generator(_) => true,
-            _ => false,
-        }
-    }
-
-    fn generator(self) -> Self {
-        match self {
-            Object::Microchip(k) => Object::Generator(k),
-            _ => self,
-        }
-    }
-}
-
-#[derive(Default, Clone)]
-struct Floor {
-    objects: HashSet<Object>,
-}
-
-impl Floor {
-    fn is_empty(&self) -> bool {
-        self.objects.is_empty()
-    }
-
-    fn is_safe(&self) -> bool {
-        for &chip in self.objects.iter().filter(|&&o| o.is_microchip()) {
-            let accompanied = self.objects.contains(&chip.generator());
-            let generator = self.objects.iter().any(|&o| o.is_generator());
-            if !accompanied && generator { return false }
-        }
-        true
-    }
-}
-
-#[derive(Clone)]
-struct State {
-    steps: u32,
-    floors: Box<[Floor]>,
-    elevator: usize,
-}
-
-impl State {
-    fn is_goal(&self) -> bool {
-        self.floors[0..3].iter().all(Floor::is_empty)
-    }
-
-    fn is_safe(&self) -> bool {
-        self.floors.iter().all(Floor::is_safe)
-    }
-
-    fn clone_up(&self) -> Self {
-        let mut state = self.clone();
-        state.steps += 1;
-        state.elevator += 1;
-        state
-    }
-
-    fn clone_down(&self) -> Self {
-        let mut state = self.clone();
-        state.steps += 1;
-        state.elevator -= 1;
-        state
-    }
-
-    fn generate_states(&self, states: &mut VecDeque<State>) {
-        for &object in &self.floors[self.elevator].objects {
-            for &other in &self.floors[self.elevator].objects {
-                if other == object { continue }
-                if self.elevator != 3 {
-                    let mut state = self.clone_up();
-                    state.floors[self.elevator].objects.remove(&object);
-                    state.floors[self.elevator].objects.remove(&other);
-                    state.floors[state.elevator].objects.insert(object);
-                    state.floors[state.elevator].objects.insert(other);
-                    if state.is_safe() { states.push_back(state) }
-                }
-                if self.elevator != 0 {
-                    let mut state = self.clone_down();
-                    state.floors[self.elevator].objects.remove(&object);
-                    state.floors[self.elevator].objects.remove(&other);
-                    state.floors[state.elevator].objects.insert(object);
-                    state.floors[state.elevator].objects.insert(other);
-                    if state.is_safe() { states.push_back(state) }
-                }
-            }
-
-            if self.elevator != 3 {
-                let mut state = self.clone_up();
-                state.floors[self.elevator].objects.remove(&object);
-                state.floors[state.elevator].objects.insert(object);
-                if state.is_safe() { states.push_back(state) }
-            }
-            if self.elevator != 0 {
-                let mut state = self.clone_down();
-                state.floors[self.elevator].objects.remove(&object);
-                state.floors[state.elevator].objects.insert(object);
-                if state.is_safe() { states.push_back(state) }
-            }
-        }
-    }
-}
-
-fn solve(floors: Vec<Floor>) -> u32 {
-    let mut visited = HashSet::new();
-    let mut states = VecDeque::new();
-    states.push_back(State { steps: 0, floors: floors.into_boxed_slice(), elevator: 0 });
-
-    while let Some(state) = states.pop_front() {
-        if state.is_goal() {
-            return state.steps;
-        }
-        let visit_floors: Vec<_> = state.floors.iter().map(|floor| {
-            let mut vec: Vec<_> = floor.objects.iter().cloned().collect();
-            vec.sort();
-            vec
-        }).collect();
-        if visited.insert((state.elevator, visit_floors)) {
-            state.generate_states(&mut states);
-        }
-    }
-
-    unreachable!()
-}
-
-fn main() {
-    // The first floor contains a promethium generator and a promethium-compatible microchip.
-    // The second floor contains a cobalt generator, a curium generator, a ruthenium generator, and a plutonium generator.
-    // The third floor contains a cobalt-compatible microchip, a curium-compatible microchip, a ruthenium-compatible microchip, and a plutonium-compatible microchip.
-    // The fourth floor contains nothing relevant.
-    const PROMETHIUM: u8 = 0;
-    const COBALT: u8 = 1;
-    const CURIUM: u8 = 2;
-    const RUTHENIUM: u8 = 3;
-    const PLUTONIUM: u8 = 4;
-    let mut floors = vec![Floor::default(); 4];
-    floors[0].objects.insert(Object::Generator(PROMETHIUM));
-    floors[0].objects.insert(Object::Microchip(PROMETHIUM));
-    floors[1].objects.insert(Object::Generator(COBALT));
-    floors[1].objects.insert(Object::Generator(CURIUM));
-    floors[1].objects.insert(Object::Generator(RUTHENIUM));
-    floors[1].objects.insert(Object::Generator(PLUTONIUM));
-    floors[2].objects.insert(Object::Microchip(COBALT));
-    floors[2].objects.insert(Object::Microchip(CURIUM));
-    floors[2].objects.insert(Object::Microchip(RUTHENIUM));
-    floors[2].objects.insert(Object::Microchip(PLUTONIUM));
-
-    println!("Part 1: {}", solve(floors.clone()));
-
-
-    // An elerium generator.
-    // An elerium-compatible microchip.
-    // A dilithium generator.
-    // A dilithium-compatible microchip.
-    const ELERIUM: u8 = 5;
-    const DILITHIUM: u8 = 6;
-    floors[0].objects.insert(Object::Generator(ELERIUM));
-    floors[0].objects.insert(Object::Microchip(ELERIUM));
-    floors[0].objects.insert(Object::Generator(DILITHIUM));
-    floors[0].objects.insert(Object::Microchip(DILITHIUM));
-    println!("Part 2: {}", solve(floors));
-}
-
-#[test]
-#[ignore]
-fn part1() {
-    // The first floor contains a hydrogen-compatible microchip and a lithium-compatible microchip.
-    // The second floor contains a hydrogen generator.
-    // The third floor contains a lithium generator.
-    // The fourth floor contains nothing relevant.
-    let mut floors = vec![Floor::default(); 4];
-    floors[0].objects.insert(Object::Microchip(0));
-    floors[0].objects.insert(Object::Microchip(1));
-    floors[1].objects.insert(Object::Generator(0));
-    floors[2].objects.insert(Object::Generator(1));
-
-    assert_eq!(11, solve(floors));
-}
diff --git a/src/bin/day12.rs b/src/bin/day12.rs
deleted file mode 100644
index 6655947..0000000
--- a/src/bin/day12.rs
+++ /dev/null
@@ -1,143 +0,0 @@
-use std::io::{self, Read};
-use std::str::FromStr;
-
-#[derive(Clone, Copy)]
-enum Operand {
-    Register(u8),
-    Immediate(i32),
-}
-
-impl FromStr for Operand {
-    type Err = ();
-    fn from_str(s: &str) -> Result<Self, ()> {
-        match s {
-            "a" => Ok(Operand::Register(0)),
-            "b" => Ok(Operand::Register(1)),
-            "c" => Ok(Operand::Register(2)),
-            "d" => Ok(Operand::Register(3)),
-            _ => Ok(Operand::Immediate(s.parse().map_err(|_| ())?)),
-        }
-    }
-}
-
-#[derive(Clone, Copy)]
-enum Operation {
-    Cpy(Operand, Operand),
-    Inc(Operand),
-    Dec(Operand),
-    Jnz(Operand, Operand),
-}
-
-impl FromStr for Operation {
-    type Err = ();
-    fn from_str(s: &str) -> Result<Self, ()> {
-        let mut iter = s.split(' ');
-        match iter.next() {
-            Some("cpy") => {
-                let src = iter.next().ok_or(())?.parse()?;
-                let dest = iter.next().ok_or(())?.parse()?;
-                Ok(Operation::Cpy(src, dest))
-            },
-            Some("inc") => Ok(Operation::Inc(iter.next().ok_or(())?.parse()?)),
-            Some("dec") => Ok(Operation::Dec(iter.next().ok_or(())?.parse()?)),
-            Some("jnz") => {
-                let cond = iter.next().ok_or(())?.parse()?;
-                let jump = iter.next().ok_or(())?.parse()?;
-                Ok(Operation::Jnz(cond, jump))
-            },
-            _ => Err(()),
-        }
-    }
-}
-
-#[derive(Default)]
-struct Vm {
-    registers: [i32; 4],
-    operations: Vec<Operation>,
-    index: i32,
-}
-
-impl FromStr for Vm {
-    type Err = ();
-    fn from_str(s: &str) -> Result<Self, ()> {
-        let mut vm = Self::default();
-        for line in s.lines() {
-            vm.operations.push(line.parse()?);
-        }
-        Ok(vm)
-    }
-}
-
-impl Vm {
-    fn step(&mut self) -> bool {
-        match self.operations[self.index as usize] {
-            Operation::Cpy(Operand::Immediate(imm), Operand::Register(reg)) => {
-                self.registers[reg as usize] = imm;
-                self.index += 1;
-            },
-            Operation::Cpy(Operand::Register(src), Operand::Register(dest)) => {
-                self.registers[dest as usize] = self.registers[src as usize];
-                self.index += 1;
-            },
-            Operation::Inc(Operand::Register(reg)) => {
-                self.registers[reg as usize] += 1;
-                self.index += 1;
-            },
-            Operation::Dec(Operand::Register(reg)) => {
-                self.registers[reg as usize] -= 1;
-                self.index += 1;
-            },
-            Operation::Jnz(Operand::Immediate(cond), Operand::Immediate(jump)) => {
-                if cond != 0 {
-                    self.index += jump;
-                } else {
-                    self.index += 1;
-                }
-            },
-            Operation::Jnz(Operand::Register(reg), Operand::Immediate(jump)) => {
-                if self.registers[reg as usize] != 0 {
-                    self.index += jump;
-                } else {
-                    self.index += 1;
-                }
-            },
-            _ => panic!("invalid operation"),
-        }
-
-        (self.index as usize) < self.operations.len()
-    }
-}
-
-fn solve1(input: &str) -> i32 {
-    let mut vm: Vm = input.parse().unwrap();
-    while vm.step() { }
-    vm.registers[0]
-}
-
-fn solve2(input: &str) -> i32 {
-    let mut vm: Vm = input.parse().unwrap();
-    vm.registers[2] = 1;
-    while vm.step() { }
-    vm.registers[0]
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve1(&input));
-    println!("Part 2: {}", solve2(&input));
-}
-
-#[test]
-fn part1() {
-    let input = "
-cpy 41 a
-inc a
-inc a
-dec a
-jnz a 2
-dec a
-";
-    assert_eq!(42, solve1(input.trim()));
-}
diff --git a/src/bin/day13.rs b/src/bin/day13.rs
deleted file mode 100644
index 060f571..0000000
--- a/src/bin/day13.rs
+++ /dev/null
@@ -1,122 +0,0 @@
-use std::collections::{HashMap, VecDeque};
-use std::io::{self, Read};
-
-#[derive(Clone, Copy, PartialEq, Eq, Hash)]
-struct Point {
-    x: u32,
-    y: u32,
-}
-
-const START: Point = Point { x: 1, y: 1 };
-
-impl Point {
-    fn north(self) -> Option<Point> {
-        if self.y == 0 {
-            None
-        } else {
-            Some(Point { x: self.x, y: self.y - 1 })
-        }
-    }
-
-    fn east(self) -> Point {
-        Point { x: self.x + 1, y: self.y }
-    }
-
-    fn south(self) -> Point {
-        Point { x: self.x, y: self.y + 1 }
-    }
-
-    fn west(self) -> Option<Point> {
-        if self.x == 0 {
-            None
-        } else {
-            Some(Point { x: self.x - 1, y: self.y })
-        }
-    }
-
-    fn neighbors(self) -> [Option<Point>; 4] {
-        [self.north(), Some(self.east()), Some(self.south()), self.west()]
-    }
-}
-
-#[derive(Clone, Copy)]
-struct Layout {
-    constant: u32,
-}
-
-impl Layout {
-    fn is_open(&self, point: Point) -> bool {
-        let Point { x, y } = point;
-        let sum = x * x + 3 * x + 2 * x * y + y + y * y + self.constant;
-        sum.count_ones() % 2 == 0
-    }
-}
-
-fn solve1(goal: Point, constant: u32) -> u32 {
-    let layout = Layout { constant: constant };
-
-    let mut distance = HashMap::new();
-    let mut frontier = VecDeque::new();
-    frontier.push_back(START);
-    distance.insert(START, 0);
-
-    while let Some(current) = frontier.pop_front() {
-        if current == goal { break; }
-        let current_distance = *distance.get(&current).unwrap();
-
-        let neighbors = current.neighbors();
-        let iter = neighbors.iter()
-            .filter_map(|&p| p)
-            .filter(|&p| layout.is_open(p));
-
-        for next in iter {
-            if !distance.contains_key(&next) {
-                frontier.push_back(next);
-                distance.insert(next, current_distance + 1);
-            }
-        }
-    }
-
-    *distance.get(&goal).unwrap()
-}
-
-fn solve2(limit: u32, constant: u32) -> usize {
-    let layout = Layout { constant: constant };
-
-    let mut distance = HashMap::new();
-    let mut frontier = VecDeque::new();
-    frontier.push_back(START);
-    distance.insert(START, 0);
-
-    while let Some(current) = frontier.pop_front() {
-        let current_distance = *distance.get(&current).unwrap();
-        if current_distance == limit { continue; }
-
-        let neighbors = current.neighbors();
-        let iter = neighbors.iter()
-            .filter_map(|&p| p)
-            .filter(|&p| layout.is_open(p));
-
-        for next in iter {
-            if !distance.contains_key(&next) {
-                frontier.push_back(next);
-                distance.insert(next, current_distance + 1);
-            }
-        }
-    }
-
-    distance.len()
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve1(Point { x: 31, y: 39 }, input.trim().parse().unwrap()));
-    println!("Part 2: {}", solve2(50, input.trim().parse().unwrap()));
-}
-
-#[test]
-fn part1() {
-    assert_eq!(11, solve1(Point { x: 7, y: 4 }, 10));
-}
diff --git a/src/bin/day14.rs b/src/bin/day14.rs
deleted file mode 100644
index 352982d..0000000
--- a/src/bin/day14.rs
+++ /dev/null
@@ -1,87 +0,0 @@
-extern crate crypto;
-
-use std::collections::VecDeque;
-use std::io::{self, Read};
-use std::iter;
-
-use crypto::digest::Digest;
-use crypto::md5::Md5;
-
-trait Hash {
-    fn hash(salt: &str, index: u32) -> String;
-}
-
-struct Part1;
-impl Hash for Part1 {
-    fn hash(salt: &str, index: u32) -> String {
-        let mut md5 = Md5::new();
-        md5.input_str(salt);
-        md5.input_str(&index.to_string());
-        md5.result_str()
-    }
-}
-
-struct Part2;
-impl Hash for Part2 {
-    fn hash(salt: &str, index: u32) -> String {
-        let mut hash = Part1::hash(salt, index);
-        for _ in 0..2016 {
-            let mut md5 = Md5::new();
-            md5.input_str(&hash);
-            hash = md5.result_str();
-        }
-        hash
-    }
-}
-
-fn solve<H: Hash>(salt: &str) -> u32 {
-    let mut hashes = VecDeque::new();
-    for i in 0..1001 {
-        hashes.push_back(H::hash(salt, i));
-    }
-
-    let mut keys = 0;
-    let mut index = 0;
-    while let Some(hash) = hashes.pop_front() {
-        let triple = hash.as_bytes()
-            .windows(3)
-            .filter(|w| w[0] == w[1] && w[1] == w[2])
-            .map(|w| w[0])
-            .next();
-
-        if let Some(ch) = triple {
-            let quintuple: String = iter::repeat(ch as char).take(5).collect();
-            if hashes.iter().any(|hash| hash.contains(&quintuple)) {
-                keys += 1;
-                if keys == 64 {
-                    return index;
-                }
-            }
-        }
-
-        index += 1;
-        hashes.push_back(H::hash(salt, 1000 + index));
-    }
-
-    unreachable!()
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve::<Part1>(input.trim()));
-    println!("Part 2: {}", solve::<Part2>(input.trim()));
-}
-
-#[test]
-#[ignore]
-fn part1() {
-    assert_eq!(22728, solve::<Part1>("abc"));
-}
-
-#[test]
-#[ignore]
-fn part2() {
-    assert_eq!(22551, solve::<Part2>("abc"));
-}
diff --git a/src/bin/day15.rs b/src/bin/day15.rs
deleted file mode 100644
index ec9df7a..0000000
--- a/src/bin/day15.rs
+++ /dev/null
@@ -1,103 +0,0 @@
-use std::io::{self, Read};
-
-#[derive(Clone, Copy)]
-struct Disc {
-    positions: u32,
-    position: u32,
-}
-
-impl Disc {
-    fn rotate(&mut self) {
-        self.position = (self.position + 1) % self.positions;
-    }
-
-    fn is_open(&self) -> bool {
-        self.position == 0
-    }
-}
-
-impl<'a> From<&'a str> for Disc {
-    fn from(s: &'a str) -> Disc {
-        let mut iter = s.trim_right_matches('.').split_whitespace();
-        Disc {
-            positions: iter.nth(3).unwrap().parse().unwrap(),
-            position: iter.last().unwrap().parse().unwrap(),
-        }
-    }
-}
-
-#[derive(Clone)]
-struct Sculpture {
-    discs: Vec<Disc>,
-    time: u32,
-    capsule: usize,
-}
-
-impl Sculpture {
-    fn tick(&mut self) {
-        self.time += 1;
-        for disc in &mut self.discs {
-            disc.rotate()
-        }
-    }
-
-    fn drop_capsule(&mut self) -> bool {
-        while self.capsule < self.discs.len() {
-            self.tick();
-            if self.discs[self.capsule].is_open() {
-                self.capsule += 1;
-            } else {
-                return false;
-            }
-        }
-        true
-    }
-}
-
-impl<'a> From<&'a str> for Sculpture {
-    fn from(s: &'a str) -> Sculpture {
-        Sculpture {
-            discs: s.lines().map(Disc::from).collect(),
-            time: 0,
-            capsule: 0,
-        }
-    }
-}
-
-fn solve1(input: &str) -> u32 {
-    let mut sculpture = Sculpture::from(input);
-    loop {
-        if sculpture.clone().drop_capsule() {
-            return sculpture.time;
-        }
-        sculpture.tick();
-    }
-}
-
-fn solve2(input: &str) -> u32 {
-    let mut sculpture = Sculpture::from(input);
-    sculpture.discs.push(Disc { positions: 11, position: 0 });
-    loop {
-        if sculpture.clone().drop_capsule() {
-            return sculpture.time;
-        }
-        sculpture.tick();
-    }
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve1(&input));
-    println!("Part 2: {}", solve2(&input));
-}
-
-#[test]
-fn part1() {
-    let input = "
-Disc #1 has 5 positions; at time=0, it is at position 4.
-Disc #2 has 2 positions; at time=0, it is at position 1.
-";
-    assert_eq!(5, solve1(input.trim()));
-}
diff --git a/src/bin/day16.rs b/src/bin/day16.rs
deleted file mode 100644
index 247d9fd..0000000
--- a/src/bin/day16.rs
+++ /dev/null
@@ -1,51 +0,0 @@
-use std::io::{self, Read};
-
-fn fill_disk(initial_state: &str, len: usize) -> String {
-    let mut state = String::from(initial_state);
-    while state.len() < len {
-        let mut b: Vec<u8> = state.bytes()
-            .map(|b| {
-                match b {
-                    b'0' => b'1',
-                    b'1' => b'0',
-                    _ => b,
-                }
-            })
-            .collect();
-        b.reverse();
-        let b = String::from_utf8(b).unwrap();
-        state.push('0');
-        state.push_str(&b);
-    }
-    state.truncate(len);
-    state
-}
-
-fn checksum(data: &str) -> String {
-    let mut sum = String::from(data);
-    while sum.len() % 2 == 0 {
-        sum = sum.as_bytes()
-            .chunks(2)
-            .map(|c| if c[0] == c[1] { '1' } else { '0' })
-            .collect();
-    }
-    sum
-}
-
-fn solve(len: usize, initial_state: &str) -> String {
-    let data = fill_disk(initial_state, len);
-    checksum(&data)
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve(272, input.trim()));
-    println!("Part 2: {}", solve(35651584, input.trim()));
-}
-
-#[test]
-fn part1() {
-    assert_eq!("01100", solve(20, "10000"));
-}
diff --git a/src/bin/day17.rs b/src/bin/day17.rs
deleted file mode 100644
index c769f32..0000000
--- a/src/bin/day17.rs
+++ /dev/null
@@ -1,148 +0,0 @@
-extern crate crypto;
-
-use std::collections::VecDeque;
-use std::io::{self, Read};
-
-use crypto::digest::Digest;
-use crypto::md5::Md5;
-
-#[derive(Clone, Copy)]
-enum Direction {
-    Up,
-    Down,
-    Left,
-    Right,
-}
-
-const DIRECTIONS: &'static [Direction] = &[
-    Direction::Up,
-    Direction::Down,
-    Direction::Left,
-    Direction::Right,
-];
-
-impl From<Direction> for char {
-    fn from(direction: Direction) -> Self {
-        match direction {
-            Direction::Up => 'U',
-            Direction::Down => 'D',
-            Direction::Left => 'L',
-            Direction::Right => 'R',
-        }
-    }
-}
-
-#[derive(Default, Clone, Copy)]
-struct Room {
-    x: u8,
-    y: u8,
-}
-
-impl Room {
-    fn neighbor(self, direction: Direction) -> Option<Room> {
-        match direction {
-            Direction::Up    if self.y != 0 => Some(Room { x: self.x, y: self.y - 1 }),
-            Direction::Down  if self.y != 3 => Some(Room { x: self.x, y: self.y + 1 }),
-            Direction::Left  if self.x != 0 => Some(Room { x: self.x - 1, y: self.y }),
-            Direction::Right if self.x != 3 => Some(Room { x: self.x + 1, y: self.y }),
-            _ => None,
-        }
-    }
-
-    fn is_vault(self) -> bool {
-        self.x == 3 && self.y == 3
-    }
-}
-
-#[derive(Default, Clone)]
-struct State {
-    room: Room,
-    path: Vec<Direction>,
-}
-
-impl<'a> From<&'a State> for String {
-    fn from(state: &'a State) -> String {
-        state.path.iter().cloned().map(char::from).collect()
-    }
-}
-
-impl State {
-    fn is_vault(&self) -> bool {
-        self.room.is_vault()
-    }
-
-    fn generate_states(&self, states: &mut VecDeque<State>, passcode: &str) {
-        let mut md5 = Md5::new();
-        md5.input_str(passcode);
-        md5.input_str(&String::from(self));
-        let hash = md5.result_str();
-
-        for (direction, ch) in DIRECTIONS.iter().cloned().zip(hash.chars()) {
-            if ch < 'b' || ch > 'f' { continue }
-
-            let room = match self.room.neighbor(direction) {
-                Some(r) => r,
-                None => continue,
-            };
-
-            let mut path = self.path.clone();
-            path.push(direction);
-
-            states.push_back(State { room: room, path: path });
-        }
-    }
-}
-
-fn solve1(passcode: &str) -> String {
-    let mut states = VecDeque::new();
-    states.push_back(State::default());
-
-    while let Some(state) = states.pop_front() {
-        if state.is_vault() {
-            return String::from(&state);
-        }
-        state.generate_states(&mut states, passcode);
-    }
-
-    panic!("no path to vault")
-}
-
-fn solve2(passcode: &str) -> usize {
-    let mut states = VecDeque::new();
-    states.push_back(State::default());
-
-    let mut longest = None;
-
-    while let Some(state) = states.pop_front() {
-        if state.is_vault() {
-            longest = Some(state.clone());
-        } else {
-            state.generate_states(&mut states, passcode);
-        }
-    }
-
-    longest.unwrap().path.len()
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve1(input.trim()));
-    println!("Part 2: {}", solve2(input.trim()));
-}
-
-#[test]
-fn part1() {
-    assert_eq!("DDRRRD", solve1("ihgpwlah"));
-    assert_eq!("DDUDRLRRUDRD", solve1("kglvqrro"));
-    assert_eq!("DRURDRUDDLLDLUURRDULRLDUUDDDRR", solve1("ulqzkmiv"));
-}
-
-#[test]
-#[ignore]
-fn part2() {
-    assert_eq!(370, solve2("ihgpwlah"));
-    assert_eq!(492, solve2("kglvqrro"));
-    assert_eq!(830, solve2("ulqzkmiv"));
-}
diff --git a/src/bin/day18.rs b/src/bin/day18.rs
deleted file mode 100644
index 19f8c91..0000000
--- a/src/bin/day18.rs
+++ /dev/null
@@ -1,88 +0,0 @@
-use std::io::{self, Read};
-
-#[derive(Clone, Copy, PartialEq, Eq)]
-enum Tile {
-    Safe,
-    Trap,
-}
-
-impl From<char> for Tile {
-    fn from(ch: char) -> Tile {
-        match ch {
-            '.' => Tile::Safe,
-            '^' => Tile::Trap,
-            _ => panic!("invalid tile char {}", ch),
-        }
-    }
-}
-
-struct Map {
-    rows: Vec<Box<[Tile]>>,
-}
-
-impl Map {
-    fn generate_row(&mut self) {
-        let mut next = Vec::new();
-
-        {
-            let previous = self.rows.last().unwrap();
-
-            for (i, &center) in self.rows.last().unwrap().iter().enumerate() {
-                let left = previous.get(i.wrapping_sub(1)).cloned().unwrap_or(Tile::Safe);
-                let right = previous.get(i + 1).cloned().unwrap_or(Tile::Safe);
-
-                let tile = match (left, center, right) {
-                    (Tile::Trap, Tile::Trap, Tile::Safe) => Tile::Trap,
-                    (Tile::Safe, Tile::Trap, Tile::Trap) => Tile::Trap,
-                    (Tile::Trap, Tile::Safe, Tile::Safe) => Tile::Trap,
-                    (Tile::Safe, Tile::Safe, Tile::Trap) => Tile::Trap,
-                    _ => Tile::Safe,
-                };
-                next.push(tile);
-            }
-        }
-
-        self.rows.push(next.into_boxed_slice());
-    }
-
-    fn count_safe(&self) -> usize {
-        self.rows.iter()
-            .map(|row| {
-                row.iter()
-                    .filter(|&&t| t == Tile::Safe)
-                    .count()
-            })
-            .sum()
-    }
-}
-
-impl<'a> From<&'a str> for Map {
-    fn from(s: &'a str) -> Map {
-        let rows = s.lines()
-            .map(|l| l.chars().map(Tile::from).collect::<Vec<_>>().into_boxed_slice())
-            .collect();
-        Map { rows: rows }
-    }
-}
-
-fn solve(rows: u32, input: &str) -> usize {
-    let mut map = Map::from(input);
-    for _ in 1..rows {
-        map.generate_row();
-    }
-    map.count_safe()
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve(40, &input));
-    println!("Part 2: {}", solve(400000, &input));
-}
-
-#[test]
-fn part1() {
-    assert_eq!(6, solve(3, "..^^."));
-    assert_eq!(38, solve(10, ".^^.^.^^^^"));
-}
diff --git a/src/bin/day19.rs b/src/bin/day19.rs
deleted file mode 100644
index 9afbf39..0000000
--- a/src/bin/day19.rs
+++ /dev/null
@@ -1,35 +0,0 @@
-use std::collections::VecDeque;
-use std::io::{self, Read};
-
-#[derive(Clone, Copy)]
-struct Elf {
-    position: usize,
-    gifts: usize,
-}
-
-fn solve(count: usize) -> usize {
-    let mut circle: VecDeque<Elf> = (0..count)
-        .map(|i| Elf { position: i + 1, gifts: 1 })
-        .collect();
-
-    while circle.len() > 1 {
-        let mut current = circle.pop_front().unwrap();
-        let next = circle.pop_front().unwrap();
-        current.gifts += next.gifts;
-        circle.push_back(current);
-    }
-
-    circle.pop_front().unwrap().position
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve(input.trim().parse().unwrap()));
-}
-
-#[test]
-fn part1() {
-    assert_eq!(3, solve(5));
-}
diff --git a/src/bin/day20.rs b/src/bin/day20.rs
deleted file mode 100644
index 0416ced..0000000
--- a/src/bin/day20.rs
+++ /dev/null
@@ -1,77 +0,0 @@
-use std::io::{self, Read};
-use std::u32;
-
-fn parse_ranges(input: &str) -> Vec<(u32, u32)> {
-    let mut ranges = vec![];
-    for line in input.lines() {
-        let hyphen = line.find('-').unwrap();
-        let (start, end) = line.split_at(hyphen);
-        let start = start.parse().unwrap();
-        let end = end[1..].parse().unwrap();
-        ranges.push((start, end));
-    }
-    ranges.sort();
-    ranges
-}
-
-fn solve1(input: &str) -> u32 {
-    let ranges = parse_ranges(input);
-    let mut lowest = 0;
-
-    for &(start, end) in &ranges {
-        if lowest >= start && lowest <= end {
-            lowest = end + 1;
-        }
-    }
-
-    lowest
-}
-
-fn solve2(input: &str) -> u32 {
-    let ranges = parse_ranges(input);
-    let mut merged = vec![];
-
-    for &(start, end) in &ranges {
-        if let Some(&mut (ref mut last_start, ref mut last_end)) = merged.last_mut() {
-            if start >= *last_start && end <= *last_end {
-                continue;
-            } else if start >= *last_start && start <= *last_end + 1 && end > *last_end {
-                *last_end = end;
-                continue;
-            }
-        }
-        merged.push((start, end));
-    }
-
-    let blocked: u32 = merged.iter().map(|&(start, end)| end - start + 1).sum();
-
-    u32::MAX - blocked + 1
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve1(&input));
-    println!("Part 2: {}", solve2(&input));
-}
-
-#[test]
-fn part1() {
-    let input = "
-5-8
-0-2
-4-7
-";
-    assert_eq!(3, solve1(input.trim()));
-}
-
-#[test]
-fn part2() {
-    let input = "
-5-8
-0-2
-4-7
-";
-    assert_eq!(u32::MAX - 7, solve2(input.trim()));
-}
diff --git a/src/bin/day21.rs b/src/bin/day21.rs
deleted file mode 100644
index 982bf8a..0000000
--- a/src/bin/day21.rs
+++ /dev/null
@@ -1,144 +0,0 @@
-use std::io::{self, Read};
-
-#[derive(Clone, Copy)]
-enum Operation {
-    SwapPosition(usize, usize),
-    SwapLetter(u8, u8),
-    RotateLeft(usize),
-    RotateRight(usize),
-    RotateLetter(u8),
-    Reverse(usize, usize),
-    Move(usize, usize),
-}
-
-impl Operation {
-    fn apply(self, slice: &mut [u8]) {
-        match self {
-            Operation::SwapPosition(i, j) => slice.swap(i, j),
-            Operation::SwapLetter(a, b) => {
-                let i = slice.iter().position(|&x| x == a).unwrap();
-                let j = slice.iter().position(|&x| x == b).unwrap();
-                slice.swap(i, j);
-            },
-            Operation::RotateLeft(n) => {
-                let n = n % slice.len();
-                slice[0..n].reverse();
-                slice[n..].reverse();
-                slice.reverse();
-            },
-            Operation::RotateRight(n) => {
-                let n = n % slice.len();
-                slice.reverse();
-                slice[0..n].reverse();
-                slice[n..].reverse();
-            },
-            Operation::RotateLetter(a) => {
-                let mut n = 1 + slice.iter().position(|&x| x == a).unwrap();
-                if n >= 5 { n += 1 }
-                n %= slice.len();
-                slice.reverse();
-                slice[0..n].reverse();
-                slice[n..].reverse();
-            },
-            Operation::Reverse(i, j) => slice[i..(j + 1)].reverse(),
-            Operation::Move(i, j) if i < j => {
-                let a = slice[i];
-                for k in i..j {
-                    slice[k] = slice[k + 1];
-                }
-                slice[j] = a;
-            },
-            Operation::Move(i, j) => {
-                let a = slice[i];
-                for k in (j..i).rev() {
-                    slice[k + 1] = slice[k];
-                }
-                slice[j] = a;
-            },
-        }
-    }
-}
-
-impl<'a> From<&'a str> for Operation {
-    fn from(s: &'a str) -> Self {
-        let mut iter = s.split_whitespace();
-        match (iter.next().unwrap(), iter.next().unwrap()) {
-            ("swap", "position") => Operation::SwapPosition(
-                iter.next().unwrap().parse().unwrap(),
-                iter.nth(2).unwrap().parse().unwrap(),
-            ),
-            ("swap", "letter") => Operation::SwapLetter(
-                iter.next().unwrap().as_bytes()[0],
-                iter.nth(2).unwrap().as_bytes()[0],
-            ),
-            ("rotate", "left") => Operation::RotateLeft(iter.next().unwrap().parse().unwrap()),
-            ("rotate", "right") => Operation::RotateRight(iter.next().unwrap().parse().unwrap()),
-            ("rotate", "based") => Operation::RotateLetter(iter.nth(4).unwrap().as_bytes()[0]),
-            ("reverse", _) => Operation::Reverse(
-                iter.next().unwrap().parse().unwrap(),
-                iter.nth(1).unwrap().parse().unwrap(),
-            ),
-            ("move", _) => Operation::Move(
-                iter.next().unwrap().parse().unwrap(),
-                iter.nth(2).unwrap().parse().unwrap(),
-            ),
-            (x, y) => panic!("invalid operation {} {}", x, y),
-        }
-    }
-}
-
-fn solve1(password: &str, input: &str) -> String {
-    let mut password = password.as_bytes().to_owned();
-
-    for operation in input.lines().map(Operation::from) {
-        operation.apply(&mut password);
-    }
-
-    String::from_utf8(password).unwrap()
-}
-
-fn solve2(scrambled: &str, input: &str) -> String {
-    let operations: Vec<_> = input.lines().map(Operation::from).collect();
-
-    let mut password = "abcdefgh".as_bytes().to_owned();
-    let len = password.len();
-
-    loop {
-        let k = (0..(len - 1)).rev().find(|&k| password[k] < password [k + 1]).unwrap();
-        let l = ((k + 1)..len).rev().find(|&l| password[k] < password[l]).unwrap();
-        password.swap(k, l);
-        password[(k + 1)..].reverse();
-
-        let mut candidate = password.clone();
-        for operation in &operations {
-            operation.apply(&mut candidate);
-        }
-
-        if candidate == scrambled.as_bytes() {
-            return String::from_utf8(password).unwrap();
-        }
-    }
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve1("abcdefgh", &input));
-    println!("Part 2: {}", solve2("fbgdceah", &input));
-}
-
-#[test]
-fn part1() {
-    let input = "
-swap position 4 with position 0
-swap letter d with letter b
-reverse positions 0 through 4
-rotate left 1 step
-move position 1 to position 4
-move position 3 to position 0
-rotate based on position of letter b
-rotate based on position of letter d
-";
-    assert_eq!("decab", solve1("abcde", input.trim()));
-}
diff --git a/src/bin/day23.rs b/src/bin/day23.rs
deleted file mode 100644
index 4a54983..0000000
--- a/src/bin/day23.rs
+++ /dev/null
@@ -1,157 +0,0 @@
-use std::io::{self, Read};
-
-#[derive(Clone, Copy)]
-enum Operand {
-    Register(u8),
-    Immediate(i32),
-}
-
-impl<'a> From<&'a str> for Operand {
-    fn from(s: &'a str) -> Self {
-        match s {
-            "a" => Operand::Register(0),
-            "b" => Operand::Register(1),
-            "c" => Operand::Register(2),
-            "d" => Operand::Register(3),
-            _ => Operand::Immediate(s.parse().unwrap()),
-        }
-    }
-}
-
-#[derive(Clone, Copy)]
-enum Operation {
-    Cpy(Operand, Operand),
-    Inc(Operand),
-    Dec(Operand),
-    Jnz(Operand, Operand),
-    Tgl(Operand),
-}
-
-impl<'a> From<&'a str> for Operation {
-    fn from(s: &'a str) -> Self {
-        let mut iter = s.split_whitespace();
-        match (iter.next().unwrap(), iter.next().unwrap()) {
-            ("cpy", a) => Operation::Cpy(Operand::from(a), Operand::from(iter.next().unwrap())),
-            ("inc", a) => Operation::Inc(Operand::from(a)),
-            ("dec", a) => Operation::Dec(Operand::from(a)),
-            ("jnz", a) => Operation::Jnz(Operand::from(a), Operand::from(iter.next().unwrap())),
-            ("tgl", a) => Operation::Tgl(Operand::from(a)),
-            _ => panic!("invalid instruction {}", s),
-        }
-    }
-}
-
-impl Operation {
-    fn toggle(self) -> Self {
-        match self {
-            Operation::Inc(a) => Operation::Dec(a),
-            Operation::Dec(a) | Operation::Tgl(a) => Operation::Inc(a),
-            Operation::Jnz(a, b) => Operation::Cpy(a, b),
-            Operation::Cpy(a, b) => Operation::Jnz(a, b),
-        }
-    }
-}
-
-#[derive(Default)]
-struct Vm {
-    registers: [i32; 4],
-    operations: Vec<Operation>,
-    index: i32,
-}
-
-impl<'a> From<&'a str> for Vm {
-    fn from(s: &'a str) -> Self {
-        let mut vm = Self::default();
-        for line in s.lines() {
-            vm.operations.push(Operation::from(line));
-        }
-        vm
-    }
-}
-
-impl Vm {
-    fn step(&mut self) -> bool {
-        match self.operations[self.index as usize] {
-            Operation::Cpy(Operand::Immediate(imm), Operand::Register(reg)) => {
-                self.registers[reg as usize] = imm;
-                self.index += 1;
-            },
-            Operation::Cpy(Operand::Register(src), Operand::Register(dest)) => {
-                self.registers[dest as usize] = self.registers[src as usize];
-                self.index += 1;
-            },
-            Operation::Inc(Operand::Register(reg)) => {
-                self.registers[reg as usize] += 1;
-                self.index += 1;
-            },
-            Operation::Dec(Operand::Register(reg)) => {
-                self.registers[reg as usize] -= 1;
-                self.index += 1;
-            },
-            Operation::Jnz(Operand::Immediate(cond), Operand::Immediate(jump)) => {
-                if cond != 0 {
-                    self.index += jump;
-                } else {
-                    self.index += 1;
-                }
-            },
-            Operation::Jnz(Operand::Register(reg), Operand::Immediate(jump)) => {
-                if self.registers[reg as usize] != 0 {
-                    self.index += jump;
-                } else {
-                    self.index += 1;
-                }
-            },
-            Operation::Jnz(Operand::Immediate(cond), Operand::Register(reg)) => {
-                if cond != 0 {
-                    self.index += self.registers[reg as usize];
-                } else {
-                    self.index += 1;
-                }
-            },
-            Operation::Tgl(Operand::Register(reg)) => {
-                let index = self.index + self.registers[reg as usize];
-                if let Some(operation) = self.operations.get_mut(index as usize) {
-                    *operation = operation.toggle();
-                }
-                self.index += 1;
-            },
-            _ => {
-                self.index += 1;
-            },
-        }
-
-        (self.index as usize) < self.operations.len()
-    }
-}
-
-fn solve(initial: i32, input: &str) -> i32 {
-    let mut vm = Vm::from(input);
-    vm.registers[0] = initial;
-    while vm.step() { }
-    vm.registers[0]
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve(7, &input));
-    println!("Part 2: {}", solve(12, &input));
-}
-
-#[test]
-fn part1() {
-    let input = "
-cpy 2 a
-tgl a
-tgl a
-tgl a
-cpy 1 a
-dec a
-dec a
-";
-    let mut vm = Vm::from(input.trim());
-    while vm.step() { }
-    assert_eq!(3, vm.registers[0]);
-}
diff --git a/src/bin/day25.rs b/src/bin/day25.rs
deleted file mode 100644
index 9ecf487..0000000
--- a/src/bin/day25.rs
+++ /dev/null
@@ -1,163 +0,0 @@
-use std::io::{self, Read};
-use std::iter;
-
-#[derive(Clone, Copy)]
-enum Operand {
-    Register(u8),
-    Immediate(i32),
-}
-
-impl<'a> From<&'a str> for Operand {
-    fn from(s: &'a str) -> Self {
-        match s {
-            "a" => Operand::Register(0),
-            "b" => Operand::Register(1),
-            "c" => Operand::Register(2),
-            "d" => Operand::Register(3),
-            _ => Operand::Immediate(s.parse().unwrap()),
-        }
-    }
-}
-
-#[derive(Clone, Copy)]
-enum Operation {
-    Cpy(Operand, Operand),
-    Inc(Operand),
-    Dec(Operand),
-    Jnz(Operand, Operand),
-    Tgl(Operand),
-    Out(Operand),
-}
-
-impl<'a> From<&'a str> for Operation {
-    fn from(s: &'a str) -> Self {
-        let mut iter = s.split_whitespace();
-        match (iter.next().unwrap(), iter.next().unwrap()) {
-            ("cpy", a) => Operation::Cpy(Operand::from(a), Operand::from(iter.next().unwrap())),
-            ("inc", a) => Operation::Inc(Operand::from(a)),
-            ("dec", a) => Operation::Dec(Operand::from(a)),
-            ("jnz", a) => Operation::Jnz(Operand::from(a), Operand::from(iter.next().unwrap())),
-            ("tgl", a) => Operation::Tgl(Operand::from(a)),
-            ("out", a) => Operation::Out(Operand::from(a)),
-            _ => panic!("invalid instruction {}", s),
-        }
-    }
-}
-
-impl Operation {
-    fn toggle(self) -> Self {
-        match self {
-            Operation::Inc(a) => Operation::Dec(a),
-            Operation::Dec(a) | Operation::Tgl(a) => Operation::Inc(a),
-            Operation::Jnz(a, b) => Operation::Cpy(a, b),
-            Operation::Cpy(a, b) => Operation::Jnz(a, b),
-            _ => unimplemented!(),
-        }
-    }
-}
-
-#[derive(Default)]
-struct Vm {
-    registers: [i32; 4],
-    operations: Vec<Operation>,
-    index: i32,
-    output: Vec<i32>,
-}
-
-impl<'a> From<&'a str> for Vm {
-    fn from(s: &'a str) -> Self {
-        let mut vm = Self::default();
-        for line in s.lines() {
-            vm.operations.push(Operation::from(line));
-        }
-        vm
-    }
-}
-
-impl Vm {
-    fn step(&mut self) -> bool {
-        match self.operations[self.index as usize] {
-            Operation::Cpy(Operand::Immediate(imm), Operand::Register(reg)) => {
-                self.registers[reg as usize] = imm;
-                self.index += 1;
-            },
-            Operation::Cpy(Operand::Register(src), Operand::Register(dest)) => {
-                self.registers[dest as usize] = self.registers[src as usize];
-                self.index += 1;
-            },
-            Operation::Inc(Operand::Register(reg)) => {
-                self.registers[reg as usize] += 1;
-                self.index += 1;
-            },
-            Operation::Dec(Operand::Register(reg)) => {
-                self.registers[reg as usize] -= 1;
-                self.index += 1;
-            },
-            Operation::Jnz(Operand::Immediate(cond), Operand::Immediate(jump)) => {
-                if cond != 0 {
-                    self.index += jump;
-                } else {
-                    self.index += 1;
-                }
-            },
-            Operation::Jnz(Operand::Register(reg), Operand::Immediate(jump)) => {
-                if self.registers[reg as usize] != 0 {
-                    self.index += jump;
-                } else {
-                    self.index += 1;
-                }
-            },
-            Operation::Jnz(Operand::Immediate(cond), Operand::Register(reg)) => {
-                if cond != 0 {
-                    self.index += self.registers[reg as usize];
-                } else {
-                    self.index += 1;
-                }
-            },
-            Operation::Tgl(Operand::Register(reg)) => {
-                let index = self.index + self.registers[reg as usize];
-                if let Some(operation) = self.operations.get_mut(index as usize) {
-                    *operation = operation.toggle();
-                }
-                self.index += 1;
-            },
-            Operation::Out(Operand::Immediate(imm)) => {
-                self.output.push(imm);
-                self.index += 1;
-            },
-            Operation::Out(Operand::Register(reg)) => {
-                self.output.push(self.registers[reg as usize]);
-                self.index += 1;
-            },
-            _ => {
-                self.index += 1;
-            },
-        }
-
-        (self.index as usize) < self.operations.len()
-    }
-}
-
-fn solve(input: &str) -> i32 {
-    for i in 0.. {
-        let mut vm = Vm::from(input);
-        vm.registers[0] = i;
-        while vm.output.len() < 50 {
-            assert!(vm.step());
-        }
-
-        let target = iter::once(0).chain(iter::once(1)).cycle().take(50);
-        if vm.output.into_iter().eq(target) {
-            return i;
-        }
-    }
-
-    unreachable!()
-}
-
-fn main() {
-    let mut input = String::new();
-    io::stdin().read_to_string(&mut input).unwrap();
-
-    println!("Part 1: {}", solve(&input));
-}