This commit is contained in:
nora 2023-12-02 18:51:05 +01:00
commit 2cfee8a448
11 changed files with 240 additions and 0 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use nix

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
input*.txt

1
2023/day1/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
/target

7
2023/day1/Cargo.lock generated Normal file
View file

@ -0,0 +1,7 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "day1"
version = "0.1.0"

11
2023/day1/Cargo.toml Normal file
View file

@ -0,0 +1,11 @@
[package]
name = "day1"
version = "0.1.0"
edition = "2021"
[profile.release]
debug = 1
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]

12
2023/day1/README.md Normal file
View file

@ -0,0 +1,12 @@
# day 1
benchmarks:
Ensure that `input.txt` contains many, many copies of the actual input, the actual input is way too small.
`cargo build --release && hyperfine 'target/release/day1 naive' 'target/release/day1 zero_alloc' 'target/release/day1 branchless'`
```
target/release/day1 branchless ran
1.52 ± 0.06 times faster than target/release/day1 zero_alloc
7.74 ± 0.27 times faster than target/release/day1 naive
```

View file

@ -0,0 +1,76 @@
pub unsafe fn part2(input: &str) {
let sum = input
.lines()
.map(|line| {
let bytes = line.as_bytes();
let mut digits = [0_u8; 128];
assert!(bytes.len() <= digits.len());
let mut i = 0;
while i < bytes.len() {
let mut insert = |b| digits[i] |= b;
// in memory:
// o n e X X X X X
// in the integer bytes:
// X X X X X e n o
let block = bytes.as_ptr().add(i).cast::<u64>().read_unaligned().to_le();
let one = (block & ((1 << (8 * 1)) - 1)) as u8;
let three = block & ((1 << (8 * 3)) - 1);
let four = block & ((1 << (8 * 4)) - 1);
let five = block & ((1 << (8 * 5)) - 1);
const fn gorble(s: &[u8]) -> u64 {
let mut bytes = [0; 8];
let mut i = 0;
while i < s.len() {
bytes[7 - i] = s[i];
i += 1;
}
// like: u64::from_be_bytes([0, 0, 0, b't', b'h', b'g', b'i', b'e'])
u64::from_be_bytes(bytes)
}
macro_rules! check {
($const:ident $len:ident == $str:expr => $value:expr) => {
const $const: u64 = gorble($str);
insert(if $len == $const { $value } else { 0 });
};
}
insert(if one >= b'0' && one <= b'9' { one } else { 0 });
check!(EIGHT five == b"eight" => b'8');
check!(SEVEN five == b"seven" => b'7');
check!(THREE five == b"three" => b'3');
check!(FIVE four == b"five" => b'5');
check!(FOUR four == b"four" => b'4');
check!(NINE four == b"nine" => b'9');
check!(SIX three == b"six" => b'6');
check!(TWO three == b"two" => b'2');
check!(ONE three == b"one" => b'1');
i += 1;
}
let first = digits[..bytes.len()].iter().find(|&&d| d > b'0').unwrap();
let last = digits[..bytes.len()]
.iter()
.rev()
.find(|&&d| d > b'0')
.unwrap();
let first = (first - b'0') as u64;
let last = (last - b'0') as u64;
first * 10 + last
})
.sum::<u64>();
println!("part2: {sum}");
}

49
2023/day1/src/main.rs Normal file
View file

@ -0,0 +1,49 @@
use std::mem::MaybeUninit;
mod branchless;
mod naive;
mod zero_alloc;
fn main() {
let kind = std::env::args().nth(1).unwrap_or("naive".into());
let mut input = std::hint::black_box(include_str!("../input.txt")).to_owned();
input.reserve(5);
unsafe {
input
.as_mut_vec()
.spare_capacity_mut()
.fill(MaybeUninit::new(0))
};
match kind.as_str() {
"part1" => part1(&input),
"naive" => naive::part2(&input),
"zero_alloc" => zero_alloc::part2(&input),
"branchless" => unsafe { branchless::part2(&input) },
_ => {
eprintln!("error: invalid mode, must be part1,naive,zero_alloc,branchless");
std::process::exit(1);
}
}
}
fn part1(input: &str) {
let sum = input
.lines()
.map(|line| {
let mut chars = line.chars().filter(|c| c.is_ascii_digit());
let first = chars.next().unwrap();
let last = chars.next_back().unwrap_or(first);
[first, last]
.into_iter()
.collect::<String>()
.parse::<u64>()
.unwrap()
})
.sum::<u64>();
println!("part1: {sum}");
}

28
2023/day1/src/naive.rs Normal file
View file

@ -0,0 +1,28 @@
pub fn part2(input: &str) {
let sum = input
.lines()
.map(|line| {
let line = line
.replace("one", "one1one")
.replace("two", "two2two")
.replace("three", "three3three")
.replace("four", "four4four")
.replace("five", "five5five")
.replace("six", "six6six")
.replace("seven", "seven7seven")
.replace("eight", "eight8eight")
.replace("nine", "nine9nine");
let mut chars = line.chars().filter(|c| c.is_ascii_digit());
let first = chars.next().unwrap();
let last = chars.next_back().unwrap_or(first);
[first, last]
.into_iter()
.collect::<String>()
.parse::<u64>()
.unwrap()
})
.sum::<u64>();
println!("part2: {sum}");
}

View file

@ -0,0 +1,43 @@
pub fn part2(input: &str) {
let sum = input
.lines()
.map(|line| {
let bytes = line.as_bytes();
let mut i = 0;
let mut first = None;
let mut last = b'_';
let mut insert = |byte| {
if first.is_none() {
first = Some(byte);
}
last = byte;
};
while i < bytes.len() {
match bytes[i] {
b @ b'0'..=b'9' => insert(b),
b'o' if bytes.get(i..(i + 3)) == Some(b"one") => insert(b'1'),
b't' if bytes.get(i..(i + 3)) == Some(b"two") => insert(b'2'),
b't' if bytes.get(i..(i + 5)) == Some(b"three") => insert(b'3'),
b'f' if bytes.get(i..(i + 4)) == Some(b"four") => insert(b'4'),
b'f' if bytes.get(i..(i + 4)) == Some(b"five") => insert(b'5'),
b's' if bytes.get(i..(i + 3)) == Some(b"six") => insert(b'6'),
b's' if bytes.get(i..(i + 5)) == Some(b"seven") => insert(b'7'),
b'e' if bytes.get(i..(i + 5)) == Some(b"eight") => insert(b'8'),
b'n' if bytes.get(i..(i + 4)) == Some(b"nine") => insert(b'9'),
_ => {}
}
i += 1;
}
let first = (first.unwrap() - b'0') as u64;
let last = (last - b'0') as u64;
first * 10 + last
})
.sum::<u64>();
println!("part2: {sum}");
}

11
shell.nix Normal file
View file

@ -0,0 +1,11 @@
{ pkgs ? import <nixpkgs> { } }: pkgs.mkShell {
buildInputs = with pkgs; [
rustup
];
shellHook = ''
export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
'';
packages = (with pkgs; [
shellcheck
]);
}