use std::{
collections::HashMap,
ops::{BitAnd, BitOr, Not, Shl, Shr},
str::FromStr,
time::Instant,
};
trait BitOp {
fn is_bitop(&self) -> bool;
fn not_bitop(&self) -> bool;
}
impl BitOp for &str {
fn is_bitop(&self) -> bool {
matches!(*self, "AND" | "OR" | "NOT" | "LSHIFT" | "RSHIFT")
}
fn not_bitop(&self) -> bool {
!self.is_bitop()
}
}
fn ops(arg: &str) -> impl Iterator<Item = &str> {
arg.split_ascii_whitespace().filter(BitOp::is_bitop)
}
fn idents(arg: &str) -> impl Iterator<Item = &str> {
arg.split_ascii_whitespace().filter(BitOp::not_bitop)
}
struct Circuit<T> {
connections: HashMap<String, String>,
cache: HashMap<String, T>,
}
impl<T> Circuit<T>
where
T: FromStr
+ Not<Output = T>
+ BitAnd<Output = T>
+ BitOr<Output = T>
+ Shl<Output = T>
+ Shr<Output = T>
+ Copy,
{
fn new() -> Self {
Self {
connections: HashMap::new(),
cache: HashMap::new(),
}
}
fn add_connection(&mut self, arg: &str) {
let split = arg.trim().split_once(" -> ").unwrap();
self.connections
.insert(split.1.to_string(), split.0.to_string());
}
fn get(&mut self, expr: &str) -> T {
if let Some(n) = self.cache.get(expr) {
*n
} else {
let n: T = {
let mut op_stream = ops(expr);
let mut ident_stream = idents(expr);
match (op_stream.next(), ident_stream.next(), ident_stream.next()) {
(None, Some(ident), None) => match ident.parse() {
Ok(n) => n,
_ => {
let expr = self.connections.get(ident).unwrap().clone();
self.get(&expr)
}
},
(Some(op), Some(lhs), _) if op == "NOT" => !self.get(lhs),
(Some(op), Some(lhs), Some(rhs)) => {
let lhs = self.get(lhs);
let rhs = self.get(rhs);
match op {
"AND" => lhs & rhs,
"OR" => lhs | rhs,
"LSHIFT" => lhs << rhs,
"RSHIFT" => lhs >> rhs,
_ => unreachable!(),
}
}
_ => unreachable!(),
}
};
self.cache.insert(expr.to_string(), n);
n
}
}
}
pub fn d7() {
let time = Instant::now();
let arg = include_str!("../data/d7.txt");
let mut circuit = Circuit::<u16>::new();
for line in arg.lines() {
circuit.add_connection(line);
}
let p1 = circuit.get("a");
circuit
.connections
.insert(String::from("b"), p1.to_string());
circuit.cache.clear();
circuit.cache.insert(String::from("b"), p1);
let p2 = circuit.get("a");
println!("Day 7: p1: {}, p2: {}, time: {:?}", p1, p2, time.elapsed());
}