diff --git a/2025/10/p2.rs b/2025/10/p2.rs index b499124..436eaa6 100644 --- a/2025/10/p2.rs +++ b/2025/10/p2.rs @@ -1,23 +1,38 @@ -#![feature(slice_split_once, exact_div)] +#![feature(slice_split_once, exact_div, never_type)] -use std::fmt; +use std::{ + collections::{HashMap, HashSet, hash_set}, + fmt, +}; -use crate::eq::Equation; +use crate::eq::{Equation, var::VarKnowledge}; pub mod eq { use std::{ fmt, + ops::DivAssign, // ops::{AddAssign, MulAssign}, }; + use crate::gcd; + #[derive(Clone)] pub struct Equation { - parameters: Box<[i16]>, - term: i16, + parameters: Box<[i32]>, + term: i32, } - // impl MulAssign for Equation { - // fn mul_assign(&mut self, rhs: i16) { + impl DivAssign for Equation { + fn div_assign(&mut self, rhs: i32) { + self.parameters + .iter_mut() + .for_each(|param| *param = param.div_exact(rhs).unwrap()); + self.term = self.term.div_exact(rhs).unwrap(); + } + } + + // impl MulAssign for Equation { + // fn mul_assign(&mut self, rhs: i32) { // self.parameters.iter_mut().for_each(|param| *param *= rhs); // self.term *= rhs; // } @@ -35,8 +50,8 @@ pub mod eq { // } // } - impl From<(Box<[i16]>, i16)> for Equation { - fn from((parameters, term): (Box<[i16]>, i16)) -> Self { + impl From<(Box<[i32]>, i32)> for Equation { + fn from((parameters, term): (Box<[i32]>, i32)) -> Self { Self { parameters, term } } } @@ -65,15 +80,15 @@ pub mod eq { } impl Equation { - pub const fn new(parameters: Box<[i16]>, term: i16) -> Self { + pub const fn new(parameters: Box<[i32]>, term: i32) -> Self { Self { parameters, term } } - pub fn set_n_term(&mut self, n: usize, value: i16) { + pub fn set_n_term(&mut self, n: usize, value: i32) { self.parameters[n] = value; } - pub fn set_term(&mut self, value: i16) { + pub fn set_term(&mut self, value: i32) { self.term = value; } @@ -84,6 +99,22 @@ pub mod eq { } } + pub fn parameters(&self) -> &[i32] { + &self.parameters + } + + pub fn has_param(&self, param: usize) -> bool { + self.parameters.get(param).is_some_and(|&p| p != 0) + } + + pub fn param_idxs(&self) -> impl Iterator { + self.parameters + .iter() + .enumerate() + .filter(|&(_, &v)| v != 0) + .map(|(idx, _)| idx) + } + pub fn left_padded(&self) -> usize { self.parameters .iter() @@ -91,20 +122,75 @@ pub mod eq { .unwrap_or(self.degree()) } + pub fn right_padded(&self) -> Option { + self.parameters + .iter() + .rev() + .position(|&item| item != 0) + .map(|v| self.parameters.len() - v - 1) + } + pub fn degree(&self) -> usize { self.parameters.len() } + /// Call this once in a while to get the gcd of the terms and turn it down instead of + /// spiraling up. + pub fn normalize(&mut self) { + let mut the_gcd = self + .parameters + .iter() + .map(|&signed| signed.unsigned_abs()) + .reduce(gcd) + .unwrap(); + + the_gcd = gcd(the_gcd, self.term as u32); + if the_gcd != 0 { + self.div_assign(the_gcd as i32); + } + } + pub fn eliminate_by(&mut self, other: &Self) -> bool { let term_idx = self.left_padded(); - let Some(&my_term) = self.parameters.get(term_idx) else { + let Some(mut my_term) = self.parameters.get(term_idx).cloned() else { return false; }; - let Some(&other_term) = other.parameters.get(term_idx) else { + let Some(mut other_term) = other.parameters.get(term_idx).cloned() else { return false; }; + let the_gcd = gcd(my_term.unsigned_abs(), other_term.unsigned_abs()) as i32; + my_term /= the_gcd; + other_term /= the_gcd; + + // self * other_term - other * self_term + self.parameters + .iter_mut() + .zip(other.parameters.iter()) + .for_each(|(a, &b)| *a = *a * other_term - b * my_term); + + self.term = self.term * other_term - other.term * my_term; + true + } + + pub fn back_eliminate_by(&mut self, other: &Self) -> bool { + let Some(term_idx) = self.right_padded() else { + return false; + }; + + let Some(mut my_term) = self.parameters.get(term_idx).cloned() else { + return false; + }; + + let Some(mut other_term) = other.parameters.get(term_idx).cloned() else { + return false; + }; + + let the_gcd = gcd(my_term.unsigned_abs(), other_term.unsigned_abs()) as i32; + my_term /= the_gcd; + other_term /= the_gcd; + // self * other_term - other * self_term self.parameters .iter_mut() @@ -119,7 +205,95 @@ pub mod eq { self.parameters.iter().all(|&v| v == 0) } - pub fn has_known(&self) -> Option<(usize, i16)> { + // pub fn has_relation(&self) -> Option<(usize, (i32, usize))> { + // if self.term != 0 { + // return None; + // } + + // let (fonly_idx, &fonly_val) = + // self.parameters.iter().enumerate().find(|&(_, &v)| v != 0)?; + // let (sonly_idx, &sonly_val) = self + // .parameters + // .iter() + // .enumerate() + // .skip(fonly_idx + 1) + // .find(|&(_, &v)| v != 0)?; + + // if self.parameters.iter().enumerate().all(|(i, &v)| { + // if i == fonly_idx || i == sonly_idx { + // return true; + // } + + // v == 0 + // }) { + // // fonly_val * fonly_idx = -sonly_val * sonly_idx, either: + // // fonly_idx = -(sonly_val/fonly_val) * sonly_idx + // // sonly_idx = -(fonly_val/sonly_val) * fonly_idx + + // if sonly_val > fonly_val { + // Some(( + // fonly_idx, + // (-sonly_val.div_exact(fonly_val).unwrap(), sonly_idx), + // )) + // } else { + // Some(( + // sonly_idx, + // (-fonly_val.div_exact(sonly_val).unwrap(), fonly_idx), + // )) + // } + // } else { + // None + // } + // } + + // pub fn has_relation(&self) -> Option<(usize, (i32, i32, usize))> { + // let (fonly_idx, &fonly_val) = + // self.parameters.iter().enumerate().find(|&(_, &v)| v != 0)?; + // let (sonly_idx, &sonly_val) = self + // .parameters + // .iter() + // .enumerate() + // .skip(fonly_idx + 1) + // .find(|&(_, &v)| v != 0)?; + + // if self.parameters.iter().enumerate().all(|(i, &v)| { + // if i == fonly_idx || i == sonly_idx { + // return true; + // } + + // v == 0 + // }) { + // // fonly_val * fonly_idx = term - sonly_val * sonly_idx, either: + // // fonly_idx = (term/fonly_val) - (sonly_val/fonly_val) * sonly_idx + // // sonly_idx = (term/sonly_val) - (fonly_val/sonly_val) * fonly_idx + + // dbg!((fonly_val, sonly_val)); + // if sonly_val.abs() > fonly_val.abs() || fonly_val.abs() == 1 { + // dbg!(self.term, fonly_val); + // Some(( + // fonly_idx, + // ( + // self.term.div_exact(fonly_val).unwrap(), + // -sonly_val.div_exact(fonly_val).unwrap(), + // sonly_idx, + // ), + // )) + // } else { + // Some(( + // sonly_idx, + // ( + // self.term.div_exact(sonly_val).unwrap(), + // -fonly_val.div_exact(sonly_val).unwrap(), + // fonly_idx, + // ), + // )) + // } + // } else { + // None + // } + // } + + pub fn has_known(&self) -> Option<(usize, i32)> { let (only_idx, &only_val) = self.parameters.iter().enumerate().find(|&(_, &v)| v != 0)?; if self.parameters.iter().enumerate().all(|(i, &v)| { @@ -139,24 +313,34 @@ pub mod eq { None } } + + pub fn test_params(&self, params: &[u16]) -> bool { + self.parameters + .iter() + .enumerate() + .map(|(i, factor)| params[i] as i32 * factor) + .sum::() + == self.term + } } - // pub mod var { - // pub enum VarDependency + pub mod var { + #[derive(Clone, Copy)] + pub enum VarStrategy { + BruteForce, + DependantOn(!), + Known(i32), + } - // pub enum VarKnowledgeType { - // DependantOn() - // Known(i16), - // Unknown, - // } - - // pub struct VarKnowledge { - // var_idx: usize, - // type: VarKnowledgeType, - // } - // } + #[derive(Clone, Copy)] + pub struct VarKnowledge { + var_idx: usize, + strategy: VarStrategy, + } + } } +#[allow(dead_code)] mod __hide { use std::{cmp::Ordering, iter::Sum, ops::Add}; @@ -230,6 +414,108 @@ impl Machine { } } + fn gauss_back(&mut self) { + let mut do_smth = true; + while do_smth { + do_smth = false; + + for i in (0..(self.equations.len()) - 1).rev() { + for j in ((i + 1)..self.equations.len()).rev() { + if self.equations[i].right_padded() == self.equations[j].right_padded() { + // SAFETY: i != j (0..!=i), so its not the same things we're borrowing + do_smth |= unsafe { &mut *std::ptr::from_mut(&mut self.equations[i]) } + .back_eliminate_by(&self.equations[j]); + } + } + } + } + } + + fn is_known(&self) -> Result, Vec<(usize, i32)>> { + let knowns = self + .equations + .iter() + .filter_map(|eq| eq.has_known()) + .collect::>(); + + if knowns.len() == self.buttons.len() { + Ok(knowns) + } else { + Err(knowns) + } + } + + fn _find_other_single_eq_with_param( + &self, + my_i: usize, + param: usize, + ) -> Option<(usize, &Equation)> { + let iter = self + .equations + .iter() + .enumerate() + .filter(|&(idx, _)| idx != my_i) + .filter(|&(_, eq)| eq.has_param(param)); + if iter.count() == 1 { + let mut iter = self + .equations + .iter() + .enumerate() + .filter(|&(idx, _)| idx != my_i) + .filter(|&(_, eq)| eq.has_param(param)); + + Some(iter.next().unwrap()) + } else { + None + } + } + + fn _strategies(&self, hash_set: &mut HashSet, i: usize, eq1: &Equation, param: usize) { + println!("param {param} is unique here (eq1) (so other things can be made from this)"); + + let leading_param = param; + hash_set.insert(leading_param); + + for other_param in eq1.param_idxs().filter(|param| !hash_set.contains(param)) { + let mut yet_this_other_branch_hash_set = hash_set.clone(); + yet_this_other_branch_hash_set.insert(other_param); + println!(" could make {other_param} from it"); + + if let Some((i, other)) = self._find_other_single_eq_with_param(i, other_param) { + println!("{other:?}"); + self._strategies(&mut yet_this_other_branch_hash_set, i, other, other_param); + } + } + } + + fn strategies(&self) { + let strategy = HashMap::::new(); + let known_ones = HashSet::::from_iter(strategy.keys().cloned()); + + for (i, eq1) in self.equations.iter().enumerate() { + for param in eq1.param_idxs().filter(|param| !known_ones.contains(param)) { + if self + .equations + .iter() + .enumerate() + .filter(|&(j, _)| i != j) + .all(|(_, eq)| !eq.has_param(param)) + { + let mut branch_set = known_ones.clone(); + branch_set.insert(param); + self._strategies(&mut branch_set, i, eq1, param); + } + } + } + } + + // fn relations(&self) -> Vec<(usize, (i32, i32, usize))> { + // self.equations + // .iter() + // .filter_map(|eq| eq.has_relation()) + // .collect::>() + // } + fn from_slice(s: &[u8]) -> Self { let (left, right) = s.split_once(|&b| b == b']').unwrap(); @@ -259,7 +545,7 @@ impl Machine { let joltages = right[..(right.len() - 1)] .split(|&b| b == b',') - .map(|s| s.iter().fold(0, |acc, b| acc * 10 + (b - b'0') as i16)); + .map(|s| s.iter().fold(0, |acc, b| acc * 10 + (b - b'0') as i32)); let mut eqs = Vec::with_capacity(len as usize); for (pos, jolts) in joltages.enumerate() { @@ -283,6 +569,13 @@ impl Machine { len, } } + + pub fn test_params(&self, solutions: &[u16]) -> bool { + self.equations + .iter() + .rev() + .all(|eq| eq.test_params(solutions)) + } } impl fmt::Debug for Machine { @@ -308,33 +601,100 @@ pub extern "Rust" fn challenge_usize(buf: &[u8]) -> usize { .split(|&b| b == b'\n') .map(Machine::from_slice); + let mut total_count = 0; + for (i, mut machine) in machines.enumerate() { - println!("{i}: {machine:?}"); - - for eq in &machine.equations { - println!(" {eq:?} {:?}", eq.has_known()); - } - println!(); - machine.gauss(); + machine.gauss_back(); - for eq in &machine.equations { - println!(" {eq:?} {:?}", eq.has_known()); + if i != 143 { + continue; + } + match machine.is_known() { + Ok(knowns) => knowns + .iter() + .for_each(|&(_, count)| total_count += count as usize), + Err(knowns) => { + // if [6, 8, 11, 13, 16, 22].contains(&i) { + // continue; + // } + + println!("{i}: {machine:?}"); + println!("{knowns:?} {}/{}", knowns.len(), machine.len); + + let mut skips = Vec::new(); + let mut solution_buffer = vec![0; machine.buttons.len()]; + for (known_idx, known_val) in knowns { + assert!(known_val.is_positive() || known_val == 0); + solution_buffer[known_idx] = known_val.unsigned_abs(); + skips.push(known_idx); + } + + for eq in &machine.equations { + println!(" {eq:?} {:?}", eq.has_known()); + } + // let relations = machine.relations(); + // println!("relations: {:?}", relations); + machine.strategies(); + + // total_count += iter_until(&mut solution_buffer[..], &skips, |potential_sol| { + // // println!("{potential_sol:?} total {}", potential_sol.iter().sum::()); + // if machine.test_params(potential_sol) { + // Some(potential_sol.iter().sum::()) + // } else { + // None + // } + // }) as usize; + } } println!(); } - 42 + total_count } /// Just an implementation of euler's algorithm -fn gcd(mut a: u16, mut b: u16) -> u16 { +fn gcd(mut a: u32, mut b: u32) -> u32 { while b != 0 { (a, b) = (b, a % b) } a } -fn lcm(a: u16, b: u16) -> u16 { +fn lcm(a: u32, b: u32) -> u32 { a * b / gcd(a, b) } + +fn iter_until(s: &mut [u32], skip: &[usize], mut f: impl FnMut(&[u32]) -> Option) -> T { + fn iter_until_rec( + s: &mut [u32], + usable_size: usize, + skip: &[usize], + l: u32, + f: &mut impl FnMut(&[u32]) -> Option, + ) -> Option { + if usable_size == 0 { + if l == 0 { f(s) } else { None } + } else if skip.iter().any(|&pos| pos == usable_size - 1) { + iter_until_rec(s, usable_size - 1, skip, l, f) + } else { + for i in 0..=l { + s[usable_size - 1] = i; + + if let Some(ret) = iter_until_rec(s, usable_size - 1, skip, l - i, f) { + return Some(ret); + } + } + + None + } + } + + for len in 1.. { + if let Some(ret) = iter_until_rec(s, s.len(), skip, len, &mut f) { + return ret; + } + } + + unreachable!() +}