aboutsummaryrefslogtreecommitdiff
path: root/src/tests/aoc_2022_09
diff options
context:
space:
mode:
authorAria Shrimpton <me@aria.rip>2024-02-29 16:10:26 +0000
committerAria Shrimpton <me@aria.rip>2024-02-29 16:10:26 +0000
commit1b476265f3f6043529d252db5353c27ebd0507b3 (patch)
treee6b96d2b6d29f44a52e8aa3b7d78a7a891ea9f5f /src/tests/aoc_2022_09
parentbb5ec77c534f77ae27856a36e8fa459a1b19356e (diff)
rename benchmark stuff to be more consistent
Diffstat (limited to 'src/tests/aoc_2022_09')
-rw-r--r--src/tests/aoc_2022_09/Cargo.toml17
-rw-r--r--src/tests/aoc_2022_09/benches/main.rs39
-rw-r--r--src/tests/aoc_2022_09/src/lib.rs2126
-rw-r--r--src/tests/aoc_2022_09/src/types.pr.rs7
4 files changed, 2189 insertions, 0 deletions
diff --git a/src/tests/aoc_2022_09/Cargo.toml b/src/tests/aoc_2022_09/Cargo.toml
new file mode 100644
index 0000000..f806c9c
--- /dev/null
+++ b/src/tests/aoc_2022_09/Cargo.toml
@@ -0,0 +1,17 @@
+[package]
+name = "aoc-2022-09"
+version = "0.1.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[dependencies]
+rand = { workspace = true }
+primrose-library = { path = "../../crates/library" }
+
+[dev-dependencies]
+criterion = { workspace = true }
+
+[[bench]]
+name = "main"
+harness = false
diff --git a/src/tests/aoc_2022_09/benches/main.rs b/src/tests/aoc_2022_09/benches/main.rs
new file mode 100644
index 0000000..dd96209
--- /dev/null
+++ b/src/tests/aoc_2022_09/benches/main.rs
@@ -0,0 +1,39 @@
+use aoc_2022_09::{gen_moves, part1, part2};
+use criterion::{criterion_group, criterion_main, BatchSize, BenchmarkId, Criterion};
+use rand::{rngs::StdRng, SeedableRng};
+
+fn run_benches(c: &mut Criterion) {
+ let mut rng = StdRng::seed_from_u64(42);
+ for size in [100, 1000, 2000].iter() {
+ c.bench_with_input(
+ BenchmarkId::new("aoc_2022_09-part1", size),
+ size,
+ |b, &n| {
+ b.iter_batched_ref(
+ || gen_moves(&mut rng, n).collect::<Vec<_>>(),
+ |moves| part1(moves.iter()),
+ BatchSize::SmallInput,
+ )
+ },
+ );
+
+ c.bench_with_input(
+ BenchmarkId::new("aoc_2022_09-part2", size),
+ size,
+ |b, &n| {
+ b.iter_batched_ref(
+ || gen_moves(&mut rng, n).collect::<Vec<_>>(),
+ |moves| part2(moves.iter()),
+ BatchSize::SmallInput,
+ )
+ },
+ );
+ }
+}
+
+criterion_group!(
+ name = benches;
+ config = Criterion::default().sample_size(10);
+ targets = run_benches
+);
+criterion_main!(benches);
diff --git a/src/tests/aoc_2022_09/src/lib.rs b/src/tests/aoc_2022_09/src/lib.rs
new file mode 100644
index 0000000..9bb7364
--- /dev/null
+++ b/src/tests/aoc_2022_09/src/lib.rs
@@ -0,0 +1,2126 @@
+#![feature(type_alias_impl_trait)]
+
+use primrose_library::traits::*;
+use rand::Rng;
+use std::cmp::min;
+
+mod types;
+use types::*;
+
+pub fn part1<'a>(moves: impl Iterator<Item = &'a Move>) -> usize {
+ visited_from_moves::<2>(moves)
+}
+
+pub fn part2<'a>(moves: impl Iterator<Item = &'a Move>) -> usize {
+ visited_from_moves::<10>(moves)
+}
+
+fn visited_from_moves<'a, const N: usize>(moves: impl Iterator<Item = &'a Move>) -> usize {
+ let mut tail_visited = Set::default();
+ tail_visited.insert((0, 0));
+ let mut knots = [(0, 0); N];
+ for mv in moves {
+ make_move(mv, &mut knots, &mut tail_visited);
+ }
+
+ tail_visited.len()
+}
+
+fn make_move(mv: &Move, knots: &mut [Pos], tail_visited: &mut Set<Pos>) {
+ let (dir, count) = mv;
+
+ for _ in 0..*count {
+ // Move head of rope
+ match dir {
+ Direction::Up => knots[0].1 += 1,
+ Direction::Down => knots[0].1 -= 1,
+ Direction::Right => knots[0].0 += 1,
+ Direction::Left => knots[0].0 -= 1,
+ };
+
+ for front_idx in 0..knots.len() - 1 {
+ let (fx, fy) = knots[front_idx];
+ let (bx, by) = &mut knots[front_idx + 1];
+ let (dx, dy) = (fx - *bx, fy - *by);
+ if (dx.abs() == 2 && dy == 0) || (dy.abs() == 2 && dx == 0) || (dy.abs() + dx.abs() > 2)
+ {
+ *bx += (dx.signum()) * min(dx.abs(), 1);
+ *by += (dy.signum()) * min(dy.abs(), 1);
+ }
+ }
+ tail_visited.insert(knots[knots.len() - 1]);
+ }
+}
+
+pub type Pos = (i32, i32);
+pub type Move = (Direction, u8);
+
+#[derive(Debug, Clone, Copy)]
+pub enum Direction {
+ Up,
+ Down,
+ Left,
+ Right,
+}
+
+pub fn gen_moves<R: Rng>(rng: &mut R, n: usize) -> impl Iterator<Item = Move> + '_ {
+ (0..n).map(|_| {
+ let dir = match rng.gen_range(0..4) {
+ 0 => Direction::Up,
+ 1 => Direction::Down,
+ 2 => Direction::Left,
+ 3 => Direction::Right,
+ _ => unreachable!(),
+ };
+ let count = rng.gen();
+
+ (dir, count)
+ })
+}
+
+#[cfg(test)]
+mod tests {
+ use crate::*;
+
+ fn parse_moves(input: &str) -> impl Iterator<Item = Move> + '_ {
+ input.lines().map(|x| {
+ let (dir, count) = x.split_once(' ').unwrap();
+ let count = count.parse().unwrap();
+
+ (
+ match dir {
+ "L" => Direction::Left,
+ "R" => Direction::Right,
+ "U" => Direction::Up,
+ "D" => Direction::Down,
+ _ => panic!("invalid direction, {}", dir),
+ },
+ count,
+ )
+ })
+ }
+
+ #[test]
+ fn test_small() {
+ let moves = parse_moves(SMALL_INPUT).collect::<Vec<_>>();
+ assert_eq!(part1(moves.iter()), 13, "part 1");
+ assert_eq!(part2(moves.iter()), 1, "part 2");
+ }
+
+ #[test]
+ fn test_large() {
+ let moves = parse_moves(LARGE_INPUT).collect::<Vec<_>>();
+ assert_eq!(part1(moves.iter()), 6236, "part 1");
+ assert_eq!(part2(moves.iter()), 2449, "part 2");
+ }
+
+ const SMALL_INPUT: &str = "R 4
+U 4
+L 3
+D 1
+R 4
+D 1
+L 5
+R 2";
+
+ const LARGE_INPUT: &str = "L 1
+R 1
+U 1
+R 1
+L 1
+U 2
+L 2
+R 1
+U 2
+D 2
+R 2
+D 2
+R 1
+U 1
+R 2
+L 1
+D 1
+L 2
+R 1
+D 1
+R 1
+U 1
+D 1
+R 1
+D 1
+R 1
+D 2
+L 2
+D 1
+R 2
+L 1
+R 2
+U 2
+R 1
+U 1
+D 2
+U 1
+R 1
+D 1
+R 2
+D 1
+L 2
+R 1
+U 1
+L 1
+U 1
+D 1
+U 1
+D 1
+L 1
+D 1
+R 1
+D 2
+L 1
+D 1
+L 1
+D 1
+R 1
+U 1
+D 2
+R 1
+D 1
+L 2
+D 2
+U 1
+L 1
+U 1
+L 1
+R 2
+U 1
+L 1
+D 1
+R 2
+L 2
+U 1
+D 2
+R 1
+U 1
+R 2
+L 2
+U 1
+L 2
+R 2
+D 1
+L 1
+R 1
+D 2
+L 2
+U 1
+L 2
+R 1
+U 2
+D 1
+U 2
+D 2
+L 2
+D 1
+R 1
+L 2
+R 2
+L 2
+R 2
+L 2
+R 2
+L 1
+U 1
+R 2
+U 1
+D 1
+R 2
+D 2
+R 2
+D 2
+L 2
+R 2
+U 3
+R 1
+U 3
+D 3
+U 1
+L 2
+D 2
+R 3
+D 1
+R 3
+U 1
+L 3
+R 3
+L 1
+U 3
+R 3
+U 3
+R 3
+U 2
+L 2
+D 2
+R 3
+U 2
+L 2
+U 3
+L 2
+R 1
+U 3
+R 2
+U 3
+L 2
+R 2
+U 1
+L 3
+R 3
+L 3
+R 2
+L 3
+D 3
+L 2
+D 1
+R 1
+D 3
+U 3
+R 3
+D 2
+L 2
+D 2
+R 1
+D 1
+U 3
+R 1
+U 2
+L 3
+U 3
+L 2
+D 1
+L 3
+R 2
+D 2
+L 2
+D 3
+L 3
+U 1
+D 3
+U 3
+L 3
+R 2
+D 2
+R 3
+D 2
+R 3
+U 2
+R 1
+L 2
+R 2
+L 1
+D 3
+U 2
+L 2
+U 3
+L 1
+D 1
+L 2
+R 3
+L 1
+D 2
+U 1
+D 3
+U 1
+D 3
+L 1
+U 1
+R 3
+L 2
+U 3
+L 3
+U 1
+L 2
+D 1
+R 2
+L 1
+R 2
+L 2
+D 3
+U 2
+D 2
+U 3
+L 4
+D 4
+R 3
+D 4
+R 1
+L 2
+U 2
+R 1
+U 4
+D 4
+U 4
+D 4
+R 4
+L 2
+U 3
+D 1
+R 4
+D 2
+L 2
+D 1
+L 4
+R 3
+U 2
+D 4
+U 3
+R 3
+L 1
+R 1
+L 2
+D 4
+U 2
+R 1
+L 3
+D 3
+R 4
+U 2
+L 1
+D 3
+R 4
+D 2
+U 3
+D 1
+L 2
+D 2
+R 2
+D 3
+R 2
+U 2
+R 1
+L 1
+U 2
+D 2
+U 3
+D 1
+U 3
+D 1
+R 4
+D 2
+U 1
+L 3
+D 4
+R 3
+D 4
+U 2
+D 1
+R 2
+U 3
+L 3
+D 1
+U 2
+L 3
+D 1
+L 3
+D 1
+U 1
+D 4
+R 1
+D 4
+L 2
+R 3
+D 4
+R 2
+L 3
+D 4
+U 3
+L 4
+U 1
+L 3
+R 1
+L 1
+D 2
+L 1
+U 2
+D 3
+L 1
+R 4
+L 1
+D 2
+U 2
+L 3
+U 3
+L 1
+R 4
+U 3
+D 3
+R 1
+L 2
+R 4
+U 2
+L 3
+U 4
+L 2
+R 4
+D 5
+U 1
+R 2
+D 5
+L 5
+R 1
+D 3
+U 3
+L 3
+U 5
+R 1
+D 1
+R 3
+U 3
+D 1
+R 1
+L 4
+R 1
+L 3
+U 4
+L 3
+R 5
+D 3
+R 2
+U 1
+R 2
+D 2
+L 4
+R 2
+U 1
+L 3
+D 4
+R 4
+L 1
+R 3
+U 3
+D 2
+R 3
+D 3
+U 1
+L 5
+U 1
+L 5
+R 5
+D 1
+U 1
+R 5
+D 4
+L 1
+R 4
+L 5
+D 5
+L 2
+D 1
+L 3
+D 5
+L 4
+R 4
+D 1
+U 1
+D 4
+U 1
+R 4
+L 2
+D 4
+U 1
+L 3
+R 3
+L 3
+R 3
+D 2
+R 3
+L 5
+R 1
+L 1
+R 3
+D 5
+U 5
+L 3
+U 3
+L 5
+D 3
+L 2
+U 5
+D 5
+U 5
+D 4
+U 4
+R 5
+U 3
+D 5
+R 1
+L 4
+U 1
+L 4
+R 2
+D 1
+U 4
+L 5
+D 2
+R 1
+U 1
+R 3
+U 1
+R 4
+U 4
+L 1
+R 2
+U 5
+D 2
+L 5
+U 4
+D 2
+R 1
+D 1
+U 6
+D 5
+L 4
+U 5
+R 1
+L 3
+D 2
+R 3
+U 6
+L 2
+R 6
+D 1
+R 5
+D 1
+L 1
+R 2
+L 4
+R 3
+L 4
+D 6
+R 5
+U 1
+L 4
+D 2
+L 2
+D 3
+L 2
+D 5
+R 4
+D 3
+L 2
+D 4
+R 1
+U 5
+L 5
+U 5
+D 2
+L 3
+D 4
+L 3
+U 4
+L 4
+U 6
+D 6
+U 4
+R 2
+D 3
+U 1
+R 4
+D 3
+R 1
+D 2
+R 3
+L 2
+D 2
+L 4
+D 5
+L 5
+R 2
+L 1
+R 2
+D 2
+R 4
+U 4
+R 5
+L 4
+D 1
+L 6
+R 4
+D 2
+R 6
+L 6
+D 3
+R 2
+D 2
+U 4
+R 2
+U 2
+L 2
+R 5
+D 5
+U 6
+D 6
+L 5
+R 1
+U 3
+L 1
+D 1
+U 2
+R 5
+L 2
+D 2
+U 4
+R 2
+D 2
+U 1
+D 2
+R 3
+L 6
+U 1
+D 6
+R 3
+D 6
+R 1
+D 7
+R 7
+L 4
+R 4
+D 5
+U 6
+R 2
+L 3
+R 5
+L 5
+U 6
+D 3
+U 5
+D 3
+U 5
+R 7
+L 5
+U 7
+L 6
+U 5
+D 1
+L 7
+U 6
+L 3
+D 1
+L 6
+U 4
+D 1
+U 1
+R 1
+L 3
+D 6
+L 6
+R 3
+D 3
+U 2
+R 1
+D 6
+U 7
+R 6
+L 4
+R 4
+D 7
+R 1
+D 5
+U 6
+D 4
+R 6
+L 2
+U 4
+D 7
+R 5
+D 6
+U 7
+R 6
+D 6
+U 1
+D 4
+L 2
+U 2
+R 7
+U 1
+L 1
+U 1
+L 7
+D 3
+U 5
+D 5
+L 1
+D 4
+L 3
+U 7
+L 6
+R 7
+U 6
+R 1
+L 3
+R 1
+D 3
+L 7
+R 4
+L 3
+D 2
+L 7
+D 2
+R 5
+L 7
+D 6
+L 6
+D 3
+L 3
+D 5
+U 3
+L 5
+R 6
+L 1
+U 5
+R 4
+U 2
+D 3
+R 5
+L 4
+U 5
+D 7
+R 1
+L 3
+U 5
+D 3
+L 6
+R 2
+L 2
+U 6
+R 3
+U 7
+R 3
+D 1
+L 2
+U 7
+L 6
+D 3
+L 8
+R 6
+D 3
+L 5
+R 5
+U 5
+R 8
+D 7
+R 7
+U 8
+L 5
+D 6
+R 6
+L 6
+D 8
+U 1
+R 4
+L 3
+R 6
+L 6
+D 5
+R 4
+D 8
+L 4
+R 4
+D 3
+L 2
+U 6
+L 8
+R 1
+U 5
+L 4
+D 8
+R 6
+D 5
+U 6
+L 5
+R 1
+L 8
+U 7
+R 6
+D 4
+U 2
+D 8
+U 3
+D 7
+R 6
+D 8
+R 8
+U 8
+D 7
+L 3
+D 4
+L 1
+U 4
+R 4
+U 2
+D 6
+R 1
+D 8
+R 2
+D 4
+R 5
+L 1
+R 7
+L 3
+D 1
+U 1
+R 7
+U 5
+D 1
+L 2
+R 8
+D 8
+L 5
+U 8
+D 4
+L 5
+D 3
+U 2
+L 7
+R 2
+L 5
+U 3
+R 3
+U 6
+R 6
+D 4
+R 3
+U 4
+D 5
+U 6
+R 3
+L 4
+R 5
+L 4
+R 8
+U 6
+L 6
+U 1
+L 5
+R 3
+U 1
+D 5
+U 8
+R 1
+L 2
+R 3
+L 8
+U 6
+L 4
+R 3
+L 3
+R 7
+L 9
+D 5
+R 8
+L 5
+U 7
+D 7
+R 6
+L 8
+U 4
+D 5
+R 8
+L 4
+R 8
+U 9
+R 9
+L 8
+D 9
+L 9
+D 6
+R 1
+L 8
+U 4
+L 6
+U 5
+R 9
+L 5
+D 6
+R 9
+D 9
+U 6
+L 1
+U 8
+L 7
+R 9
+U 4
+R 6
+U 1
+R 5
+U 5
+L 4
+R 4
+D 7
+L 6
+U 3
+R 9
+U 1
+L 6
+U 5
+L 5
+U 2
+L 3
+U 2
+D 6
+R 8
+L 5
+U 4
+D 4
+R 4
+D 6
+L 8
+U 9
+R 4
+L 3
+D 1
+U 2
+D 5
+U 3
+D 3
+L 2
+D 3
+L 4
+D 5
+L 9
+D 9
+U 2
+D 8
+R 4
+L 8
+R 9
+D 7
+L 3
+D 2
+R 2
+L 4
+D 7
+R 6
+D 8
+R 9
+L 3
+D 5
+U 1
+L 8
+D 6
+R 9
+U 6
+R 9
+D 6
+L 9
+U 5
+D 10
+L 4
+U 10
+R 3
+U 7
+D 4
+L 3
+R 8
+L 4
+U 10
+R 8
+L 10
+U 8
+R 5
+L 8
+U 6
+L 5
+D 7
+L 6
+U 6
+L 9
+R 8
+U 6
+D 7
+L 10
+R 2
+D 7
+L 8
+U 5
+L 2
+D 2
+U 7
+D 6
+R 7
+D 2
+U 3
+R 10
+D 2
+U 6
+D 6
+L 8
+D 2
+L 2
+D 10
+L 2
+D 1
+L 10
+D 6
+R 6
+L 7
+D 9
+R 2
+D 5
+U 2
+R 4
+L 4
+D 1
+L 3
+R 10
+U 9
+L 4
+D 10
+L 10
+D 4
+U 2
+R 1
+L 4
+D 6
+U 2
+L 6
+R 4
+U 1
+L 9
+R 7
+L 8
+U 5
+R 1
+D 6
+L 4
+U 9
+D 1
+L 5
+R 10
+D 4
+L 6
+U 8
+R 4
+L 5
+D 10
+R 8
+D 8
+R 4
+D 5
+R 7
+L 10
+D 3
+R 1
+L 4
+R 8
+L 10
+D 2
+L 3
+D 6
+L 8
+R 10
+L 2
+D 8
+R 5
+L 8
+D 1
+U 6
+D 11
+U 7
+L 1
+U 6
+R 9
+L 8
+R 5
+U 9
+D 5
+L 10
+U 6
+L 3
+D 4
+U 6
+D 10
+L 2
+D 9
+L 5
+R 8
+D 1
+R 1
+U 1
+D 4
+R 4
+U 6
+L 9
+U 7
+R 11
+L 8
+D 3
+R 9
+L 11
+U 9
+L 4
+D 10
+U 9
+L 7
+R 9
+U 1
+R 8
+U 4
+L 3
+U 2
+D 7
+R 6
+U 1
+D 10
+U 9
+R 6
+L 6
+D 4
+U 3
+L 10
+R 10
+U 6
+R 8
+L 9
+R 2
+D 7
+R 4
+U 6
+R 2
+L 4
+D 1
+L 10
+U 9
+R 11
+U 11
+R 1
+D 5
+L 8
+U 3
+R 1
+U 4
+D 10
+U 6
+L 8
+R 4
+L 7
+R 8
+D 3
+L 9
+U 4
+L 6
+R 2
+D 2
+U 9
+D 6
+R 8
+L 2
+U 7
+L 2
+R 8
+L 7
+D 5
+R 10
+U 5
+L 10
+U 6
+R 8
+U 9
+L 7
+D 11
+U 10
+L 4
+D 5
+R 4
+L 7
+U 5
+D 8
+R 6
+D 9
+L 4
+U 9
+R 6
+D 1
+U 6
+R 8
+U 10
+D 6
+U 10
+L 3
+D 1
+L 1
+R 9
+D 10
+L 10
+U 12
+D 3
+L 2
+U 6
+L 9
+U 2
+R 6
+D 8
+R 11
+L 1
+U 3
+R 1
+U 9
+R 12
+U 3
+D 11
+U 3
+D 10
+U 12
+L 11
+R 9
+U 9
+D 11
+L 8
+D 4
+R 4
+U 4
+D 1
+L 7
+R 10
+L 4
+R 4
+U 11
+D 3
+L 6
+R 10
+L 9
+R 11
+U 6
+D 2
+U 2
+L 3
+D 11
+U 6
+D 12
+L 5
+R 5
+D 6
+R 5
+U 2
+R 8
+L 7
+D 7
+R 3
+D 8
+U 6
+D 1
+U 1
+D 4
+L 3
+D 12
+L 8
+D 8
+U 7
+R 1
+D 10
+L 5
+D 7
+L 1
+U 4
+R 12
+L 8
+U 2
+L 2
+U 1
+L 8
+U 3
+L 12
+R 3
+D 11
+R 1
+U 4
+D 12
+L 3
+U 2
+R 5
+U 2
+R 7
+U 8
+L 5
+R 1
+D 5
+R 12
+D 8
+R 9
+D 7
+L 2
+R 2
+U 9
+D 6
+U 6
+R 2
+D 2
+R 9
+D 4
+R 12
+D 13
+R 9
+L 3
+D 8
+L 13
+U 2
+R 10
+U 12
+D 12
+U 3
+L 6
+D 8
+U 1
+L 12
+D 9
+L 7
+R 2
+D 4
+R 5
+L 7
+R 12
+U 4
+L 7
+D 4
+U 10
+L 2
+D 1
+U 5
+R 5
+U 2
+L 9
+D 10
+L 1
+R 7
+L 6
+D 1
+L 12
+R 6
+U 10
+R 2
+D 10
+R 5
+L 3
+R 8
+D 1
+R 12
+L 7
+U 5
+L 11
+D 4
+U 11
+D 1
+U 11
+L 1
+D 11
+U 11
+D 4
+U 8
+R 5
+D 6
+L 12
+R 5
+D 10
+L 2
+D 5
+R 10
+U 6
+D 6
+R 4
+D 7
+L 2
+D 5
+U 2
+R 9
+L 11
+U 13
+D 9
+L 11
+D 8
+L 2
+D 4
+R 13
+L 5
+U 12
+R 11
+U 5
+L 8
+U 5
+D 7
+U 1
+R 9
+D 4
+U 3
+R 9
+L 3
+D 7
+R 8
+D 7
+L 2
+U 4
+L 12
+U 13
+L 12
+R 8
+L 3
+R 12
+L 4
+U 12
+R 13
+D 10
+L 8
+U 12
+R 5
+L 1
+U 2
+L 3
+U 2
+D 12
+L 12
+U 11
+L 2
+R 6
+U 3
+L 2
+R 7
+U 7
+L 3
+R 8
+L 11
+D 2
+U 1
+R 12
+D 1
+R 4
+L 13
+U 14
+R 14
+D 12
+R 12
+D 11
+L 11
+U 2
+L 6
+D 8
+L 5
+D 7
+R 2
+L 14
+U 12
+R 13
+L 7
+D 2
+U 4
+D 5
+R 5
+L 7
+R 7
+U 1
+R 10
+D 11
+L 7
+U 2
+R 5
+L 11
+U 11
+R 9
+U 9
+R 12
+D 14
+R 5
+L 9
+U 2
+D 12
+L 6
+U 7
+R 5
+L 10
+D 10
+U 4
+D 13
+L 13
+D 3
+R 8
+D 10
+U 12
+L 14
+U 7
+D 6
+R 4
+U 11
+R 2
+L 5
+D 12
+R 4
+L 4
+D 5
+L 12
+U 3
+L 3
+U 7
+R 4
+L 2
+R 7
+L 5
+D 5
+R 9
+L 5
+D 12
+R 3
+U 1
+D 6
+U 2
+R 10
+L 4
+U 12
+R 15
+L 2
+R 3
+D 8
+L 13
+D 3
+R 5
+D 5
+L 5
+U 7
+L 4
+R 10
+D 11
+L 3
+R 1
+U 4
+D 15
+L 8
+U 10
+L 5
+D 1
+R 5
+D 15
+R 3
+U 9
+R 4
+D 3
+L 15
+U 6
+D 10
+U 2
+R 6
+U 10
+L 12
+R 8
+D 12
+R 14
+U 10
+D 1
+L 1
+D 8
+U 3
+R 11
+D 7
+L 5
+D 14
+R 1
+D 8
+R 6
+U 6
+D 1
+U 14
+R 12
+L 5
+U 5
+L 14
+U 1
+D 3
+R 6
+U 4
+R 9
+L 13
+R 2
+U 10
+R 11
+U 7
+D 8
+U 14
+L 4
+R 3
+U 6
+R 2
+D 14
+U 10
+D 13
+U 8
+R 12
+L 12
+U 6
+L 4
+U 5
+D 6
+U 8
+L 12
+D 1
+L 3
+R 4
+L 11
+R 5
+D 3
+U 6
+D 15
+R 14
+U 8
+R 10
+U 15
+R 13
+L 13
+U 4
+D 3
+U 4
+D 1
+U 5
+R 5
+D 4
+R 2
+D 13
+R 7
+D 10
+U 3
+R 15
+D 3
+U 13
+R 16
+U 2
+D 6
+L 13
+R 8
+L 5
+R 1
+U 7
+R 6
+U 6
+R 10
+U 5
+L 14
+R 12
+U 8
+R 13
+U 4
+L 10
+R 16
+L 15
+D 7
+R 14
+D 8
+U 8
+L 15
+R 2
+U 11
+L 6
+R 6
+D 2
+U 9
+D 12
+L 10
+U 7
+R 8
+L 4
+D 5
+R 13
+L 9
+U 15
+D 11
+R 11
+D 8
+L 10
+R 14
+D 3
+R 10
+U 1
+R 11
+L 16
+R 12
+D 15
+L 13
+U 2
+L 11
+U 15
+D 9
+R 1
+D 13
+L 16
+U 9
+D 2
+R 5
+U 9
+R 11
+L 16
+U 5
+R 11
+U 8
+L 15
+U 1
+L 11
+U 3
+L 4
+U 9
+L 5
+U 11
+D 9
+U 4
+L 14
+U 8
+L 16
+D 9
+U 5
+L 14
+R 13
+U 8
+L 4
+R 9
+L 4
+R 7
+D 15
+U 14
+D 8
+U 6
+R 11
+L 15
+D 10
+R 2
+L 12
+R 11
+D 7
+R 11
+L 4
+R 4
+D 4
+R 3
+U 15
+L 6
+R 8
+L 5
+U 13
+D 6
+R 1
+D 15
+L 9
+U 2
+L 13
+R 10
+U 8
+R 15
+D 16
+L 13
+U 13
+L 12
+R 10
+U 16
+L 10
+R 14
+D 17
+R 4
+D 16
+U 17
+R 8
+D 10
+R 11
+L 10
+U 17
+L 11
+U 3
+L 4
+U 2
+D 10
+R 15
+U 1
+R 5
+L 12
+D 4
+R 1
+U 15
+R 13
+L 17
+R 15
+D 4
+L 14
+R 3
+D 1
+U 14
+R 11
+D 12
+L 7
+D 11
+R 12
+U 1
+R 5
+L 11
+D 9
+U 8
+R 1
+U 4
+L 15
+R 12
+U 17
+R 4
+U 14
+L 4
+D 16
+R 9
+U 9
+D 11
+L 7
+D 16
+R 10
+L 10
+R 15
+U 10
+R 6
+L 4
+R 6
+L 12
+U 9
+R 15
+D 12
+L 14
+U 14
+D 6
+U 4
+R 17
+U 2
+L 5
+R 13
+D 15
+U 13
+L 5
+D 3
+L 7
+U 12
+R 13
+D 15
+U 15
+L 17
+U 17
+L 6
+R 2
+U 17
+R 7
+L 15
+U 17
+R 11
+L 2
+U 1
+D 4
+U 2
+R 10
+L 9
+D 18
+U 5
+D 7
+R 18
+L 11
+D 2
+U 13
+D 12
+R 10
+D 2
+R 9
+L 3
+D 9
+R 9
+L 17
+R 4
+L 7
+D 16
+L 5
+R 3
+L 8
+U 7
+R 8
+L 1
+D 12
+U 13
+R 8
+D 18
+U 7
+R 6
+D 7
+L 7
+D 8
+R 16
+L 13
+R 6
+U 1
+L 9
+U 9
+L 12
+D 13
+R 18
+L 7
+D 18
+U 17
+R 18
+D 11
+R 1
+D 4
+U 3
+L 7
+D 17
+R 8
+U 11
+D 7
+L 4
+R 12
+U 10
+L 15
+R 14
+U 16
+D 4
+L 10
+U 6
+D 18
+R 7
+U 13
+R 11
+D 14
+L 5
+U 5
+L 7
+R 15
+L 16
+U 6
+D 9
+L 17
+R 4
+L 16
+D 10
+U 16
+D 17
+R 7
+L 16
+U 6
+L 15
+R 13
+U 8
+L 14
+R 12
+U 5
+D 17
+R 13
+D 12
+U 12
+L 16
+R 8
+U 4
+D 6
+U 10
+D 14
+R 18
+L 17
+U 15
+L 5
+U 7
+D 15
+U 10
+R 18
+L 11
+D 10
+R 12
+U 15
+D 11
+U 7
+D 3
+R 2
+U 5
+R 12
+U 15
+R 12
+U 19
+L 6
+U 18
+D 8
+L 7
+D 2
+R 1
+L 16
+D 5
+L 9
+U 11
+L 6
+U 7
+L 2
+D 18
+L 1
+U 19
+D 15
+U 8
+R 5
+U 13
+L 7
+R 14
+U 2
+D 10
+U 16
+D 3
+R 8
+L 10
+D 17
+U 16
+D 6
+R 18
+D 5
+L 5
+D 4
+L 6
+U 17
+D 7
+R 15
+D 5
+R 18
+L 6
+U 15
+L 3
+U 13
+L 15
+R 5
+D 9
+R 11
+U 15
+L 19
+D 11
+L 8
+D 14
+R 7
+L 16
+D 5
+R 13
+D 5
+L 9
+D 3
+L 3
+R 12
+U 17
+R 8
+L 11
+U 2
+R 9
+L 3
+D 6
+L 9
+U 11
+R 6
+U 19
+R 6
+L 12
+U 5
+R 4
+U 4
+L 16
+R 17
+D 12
+U 11
+R 4
+D 11
+U 4
+R 8
+U 9
+R 12
+U 13
+L 2
+D 9
+L 2";
+}
diff --git a/src/tests/aoc_2022_09/src/types.pr.rs b/src/tests/aoc_2022_09/src/types.pr.rs
new file mode 100644
index 0000000..d4225a9
--- /dev/null
+++ b/src/tests/aoc_2022_09/src/types.pr.rs
@@ -0,0 +1,7 @@
+/*SPEC*
+property unique<T> {
+ \c <: (Container) -> ((for-all-elems c) \a -> ((unique-count? a) c))
+}
+
+type Set<T> = {c impl (Container) | (unique c)}
+ *ENDSPEC*/