aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/day6.rs693
-rw-r--r--src/lib.rs1
-rw-r--r--src/ring_buffer.rs121
3 files changed, 815 insertions, 0 deletions
diff --git a/src/day6.rs b/src/day6.rs
new file mode 100644
index 0000000..980690b
--- /dev/null
+++ b/src/day6.rs
@@ -0,0 +1,693 @@
+use aoc_runner_derive::*;
+use std::fmt::{Display, Formatter};
+use std::error::Error;
+
+use std::ops::{Add, Mul, Index, IndexMut};
+
+use boxed_array_ringbuffer::RingBuffer;
+
+
+const PART1COUNT : usize = 80;
+const PART2COUNT : usize = 256;
+
+
+#[derive(Clone)]
+pub struct Fishtank {
+ adults : RingBuffer<usize,7>,
+ babies : RingBuffer<usize,2>,
+}
+
+impl Fishtank {
+ fn count(&self) -> usize {
+ self.adults.iter().chain(self.babies.iter()).sum()
+ }
+ fn progress(self) -> Self {
+ let births = self.adults[0];
+ let (babies, grown_ups) = self.babies.push_pop(births);
+ let new_adults = self.adults[0] + grown_ups;
+ let (adults, _) = self.adults.push_pop(new_adults);
+ Self{adults,babies}
+ }
+}
+
+fn let_fishtank_grow_for_days(fishtank : Fishtank, days : usize) -> Fishtank {
+ (0..days).fold(fishtank, |fishtank,_| fishtank.progress())
+}
+
+#[derive(Debug)]
+pub struct ParseInputError;
+impl Display for ParseInputError {
+ fn fmt(&self, f : &mut Formatter) -> Result<(),std::fmt::Error> {
+ write!(f,"Failed to parse input")
+ }
+}
+impl Error for ParseInputError {}
+#[aoc_generator(day6)]
+pub fn input_generator(input : &str) -> Result<Fishtank, ParseInputError> {
+ let adults = input.split(",").map(str::parse).try_fold(RingBuffer::new_copy(0), |mut buffer, age| {
+ match age {
+ Ok(age) => {
+ if let Some(entry) = buffer.get_mut(age) {
+ *entry +=1;
+ Ok(buffer)
+ }
+ else {
+ Err(ParseInputError)
+ }
+ }
+ Err(_) => { Err(ParseInputError) }
+ }
+ })?;
+ Ok(Fishtank {
+ adults : adults,
+ babies : RingBuffer::new_copy(0)
+ })
+}
+
+#[aoc(day6,part1, fishtank)]
+fn solve_day6_part1_fishtank(input : &Fishtank) -> usize {
+ let_fishtank_grow_for_days(input.clone(), PART1COUNT).count()
+}
+
+#[aoc(day6,part2, fishtank)]
+fn solve_day6_part2_fishtank(input : &Fishtank) -> usize {
+ let_fishtank_grow_for_days(input.clone(), PART2COUNT).count()
+}
+
+//-------------------------------------------------------
+//Closed solution derivation:
+//
+//First we implement matrix multiplication, because we don't want third party dependencies here.
+
+#[derive(Clone, Debug)]
+struct Matrix<T, const COLUMNS : usize, const ROWS : usize>
+where T : Copy+Add<Output=T>+Mul<Output=T>+Default+std::iter::Sum
+{
+ storage : [[T; COLUMNS]; ROWS],
+}
+
+impl<T, const COLUMNS : usize, const ROWS : usize> Default for Matrix<T, COLUMNS, ROWS>
+where T : Copy+Add<Output=T>+Mul<Output=T>+Default+std::iter::Sum
+{
+ fn default() -> Self { Self {storage : [[T::default(); COLUMNS]; ROWS]} }
+}
+
+impl<T, const COLUMNS : usize, const ROWS : usize> Index<usize> for Matrix<T, COLUMNS, ROWS>
+ where T : Copy+Add<Output=T>+Mul<Output=T>+Default+std::iter::Sum
+{
+ type Output = [T; COLUMNS];
+ fn index(&self, index : usize) -> &Self::Output {
+ &self.storage[index]
+ }
+}
+
+impl<T, const COLUMNS : usize, const ROWS : usize> IndexMut<usize> for Matrix<T, COLUMNS, ROWS>
+ where T: Copy+Add<Output=T>+Mul<Output=T>+Default+std::iter::Sum
+{
+ fn index_mut(&mut self, index : usize) -> &mut Self::Output {
+ &mut self.storage[index]
+ }
+}
+
+fn dot<T, const R1 : usize, const C1R2 : usize, const C2 : usize>(l : Matrix<T, C1R2, R1>, r : Matrix<T, C2, C1R2>) -> Matrix<T, C2, R1>
+where T : Copy+Add<Output=T>+Mul<Output=T>+Default+std::iter::Sum
+{
+ //the Matrix::default() call is ugly and wastes cycles, but afaict the only currently sane way to pull this
+ //off... There'd be the https://docs.rs/array-init/ crate, or I could write my own unsafe code,
+ //or use non-inline storage, but that all sounds ugly.
+ (0..R1).flat_map(|row| (0..C2).map(move |column| (row, column))).fold(Matrix::default(), |mut result, (row, column)| {
+ result[row][column] = l[row].iter().zip(0..C1R2).map(|(rv, cindex)| (rv, r[cindex][column])).map(|(&a,b)| a*b).sum();
+ result
+ })
+}
+
+impl<T, const R1 : usize, const C1R2 : usize, const C2 : usize> Mul<Matrix<T, C2, C1R2>> for Matrix<T, C1R2, R1>
+where T : Copy+Add<Output=T>+Mul<Output=T>+Default+std::iter::Sum
+{
+ type Output = Matrix<T, C2, R1>;
+ fn mul(self, rhs : Matrix<T, C2, C1R2>) -> Self::Output {
+ dot(self, rhs)
+ }
+}
+
+impl<T, const CR : usize> std::iter::Product for Matrix<T, CR, CR>
+where T : Copy+Add<Output=T>+Mul<Output=T>+Default+std::iter::Sum
+{
+ fn product<I>(iter: I) -> Self
+ where I: Iterator<Item = Self>
+ {
+ iter.reduce(|lhs, rhs| lhs * rhs).unwrap_or_default()
+ }
+}
+
+fn diag_matrix<T, const RC : usize>(input : [T; RC]) -> Matrix<T, RC, RC>
+where T : Copy+Add<Output=T>+Mul<Output=T>+Default+std::iter::Sum
+{
+ (0..RC).fold(Matrix::default(), |mut result, index| {
+ result[index][index] = input[index];
+ result
+ })
+}
+
+
+//---------------------------------------------------------
+//Now we implement a solution based on matrix multiplication.
+
+fn get_fish_count_after_days_using_matrices(input : Matrix<usize, 1, 9>, days : usize) -> usize {
+ let sum_up : Matrix<usize,9,1>
+ = Matrix { storage : [[1,1,1,1,1,1,1,1,1]] };
+
+ let progress_per_day : Matrix<usize, 9, 9> = Matrix { storage : [
+ [0,1,0,0,0,0,0,0,0],
+ [0,0,1,0,0,0,0,0,0],
+ [0,0,0,1,0,0,0,0,0],
+ [0,0,0,0,1,0,0,0,0],
+ [0,0,0,0,0,1,0,0,0],
+ [0,0,0,0,0,0,1,0,0],
+ [1,0,0,0,0,0,0,1,0],
+ [0,0,0,0,0,0,0,0,1],
+ [1,0,0,0,0,0,0,0,0]
+ ] };
+
+ let total_progress = std::iter::repeat(progress_per_day).take(days).product();
+
+ (sum_up * total_progress * input)[0][0]
+}
+
+
+
+#[aoc(day6, part1, Matrices)]
+fn solve_part1_matrices(input : &Fishtank) -> usize {
+ //this is ugly, but I don't have the concentration today to make this better...
+ let adults = &input.adults;
+ let babies = &input.babies;
+ let initial_state : Matrix<usize, 1, 9>
+ = Matrix { storage : [[adults[0]], [adults[1]], [adults[2]], [adults[3]], [adults[4]], [adults[5]], [adults[6]], [babies[0]], [babies[1]]] };
+
+ get_fish_count_after_days_using_matrices(initial_state, PART1COUNT)
+}
+
+#[aoc(day6, part2, Matrices)]
+fn solve_part2_matrices(input : &Fishtank) -> usize {
+ //this is ugly, but I don't have the concentration today to make this better...
+ let adults = &input.adults;
+ let babies = &input.babies;
+ let initial_state : Matrix<usize, 1, 9>
+ = Matrix { storage : [[adults[0]], [adults[1]], [adults[2]], [adults[3]], [adults[4]], [adults[5]], [adults[6]], [babies[0]], [babies[1]]] };
+
+ get_fish_count_after_days_using_matrices(initial_state, PART2COUNT)
+}
+
+//----------------------------------------------------------
+//We now know that the above matrix indeed yields the correct solution. However multiplying 9x9
+//matrices is slow. Sooo, let's find its Eigenvectors to get rid of those nasty matrix
+//multiplications...
+//First things first: it can already be seen by just looking at the matrix, that it's linearly
+//independent. This means that it definitely can be diagonalized. However the Eigenvalues are
+//likely complex, as the matrix is obviously not Hermitian.
+//
+//I'll now not implement an Eigenvalue solver here, as that's definitely beyond the scope of this
+//exercise. I'll just document the steps of that calculation:
+//
+//First we need to find the characteristic polynomial. That's given by det(λ*E - M) = 0 where E is
+//the Identity Matrix, M is the matrix we want to diagonalize, and λ is the variable we solve this
+//characteristic polynomial for, each solution of which is an Eigenvalue.
+//
+//After typing the above equation into a calculator (in my case: my trusty TI-92), the
+//characteristic polynomial is revealed: λ⁹ - λ² - 1 = 0
+//
+//Well, isn't that nice? A polynomial that doesn't yield at all to analytic solutions...
+//We'll have to go with approximate solutions... If I were to implement a solver myself, I'd just
+//go with the Weierstraß (also known as Durand-Kerner) method. However I'm not going to do that and
+//rather just rely on a a website that implements the Weierstraß-Method to compute the roots:
+//http://www.hvks.com/Numerical/websolver.php
+//
+//The roots are (after fixing symmetry around real axis):
+//X1=(-0.9961306220554406+i0.41731183633579305)
+//X2=(-0.9961306220554406-i0.41731183633579305)
+//X3=(-0.37921398065481077+i0.892877546086168)
+//X4=(-0.37921398065481077-i0.892877546086168)
+//X5=(0.7340778984637529+i0.742065121962188)
+//X6=(0.7340778984637529-i0.742065121962188)
+//X7=(0.09575446900611988-i0.8701987186721044)
+//X8=(0.09575446900611988+i0.8701987186721044)
+//X9=(1.091024470480757)
+//
+//Now we need the Eigenvector for each of them. Those are defined as those vectors, that are just
+//scaled by multiplying them with the matrix, with their scaling being the Eigenvalue.
+//
+//In other words, we have to solve the coupled linear equations M.v = λ*v for each value of λ.
+//We need an additional constraint for that system of equations to be fully defined, and we'll pick
+//that the Eigenvectors need to be normalized.
+//
+//I'm honestly too lazy to do that by hand. Wolfram Alpha spits out nice eigenvectors, but
+//precision is lacking. I'll rather use LAPACK and GNU Maxima for that part...
+//Soo, dgeev() is the way to go...
+//
+//In any case, I'll from now on use the output of GNU Maxmia and copy&paste stuff as needed, because
+//frankly, the closed form is frightening.
+//
+//We also need a way to deal with complex numbers... I could drag in a third-party crate, but the
+//only operations we need are addition, multiplication and exponents.
+//
+//I'll do a minimal (and slow) implementation below...
+
+//Returns an array because I don't want to implement matrix iterators...
+fn get_progress_matrix_eigenvalues() -> [Complex; 9] {
+ [
+ Complex::new(-0.9961306220554396, 0.4173118363357923),
+ Complex::new(-0.9961306220554396, -0.4173118363357923),
+ Complex::new(1.091024470480757, 0.0),
+ Complex::new(0.7340778984637535, 0.7420651219621887),
+ Complex::new(0.7340778984637535, -0.7420651219621887),
+ Complex::new(-0.3792139806548112, 0.8928775460861691),
+ Complex::new(-0.3792139806548112, -0.8928775460861691),
+ Complex::new(0.09575446900611977, 0.8701987186721045),
+ Complex::new(0.09575446900611977, -0.8701987186721045)
+ ]
+}
+
+fn get_progress_matrix_eigenvectors() -> Matrix<Complex,9,9> {
+ Matrix { storage : { [
+ [
+ Complex::new(0.19899984657465, -0.1896211561334271),
+ Complex::new(0.19899984657465, 0.1896211561334271),
+ Complex::new(0.2664863857010273, 0.0),
+ Complex::new(-0.009810288227828924, -0.3020749692844217),
+ Complex::new(-0.009810288227828924, 0.3020749692844217),
+ Complex::new(-0.2444548828945468, -0.2533423718859127),
+ Complex::new(-0.2444548828945468, 0.2533423718859127),
+ Complex::new(-0.3792659279170416, 0.08448995460471606),
+ Complex::new(-0.3792659279170416, -0.08448995460471606)
+ ],
+ [
+ Complex::new(-0.1190986880831869, 0.2719324316186708),
+ Complex::new(-0.1190986880831869, -0.2719324316186708),
+ Complex::new(0.290743167849794, 0.0),
+ Complex::new(0.2169577831581597, -0.2290264313610791),
+ Complex::new(0.2169577831581597, 0.2290264313610791),
+ Complex::new(0.3189044245620895, -0.1221973066562757),
+ Complex::new(0.3189044245620895, 0.1221973066562757),
+ Complex::new(-0.1098394577774974, -0.32194643376987),
+ Complex::new(-0.1098394577774974, 0.32194643376987)
+ ],
+ [
+ Complex::new(0.005157227848246945, -0.3205815144945338),
+ Complex::new(0.005157227848246945, 0.3205815144945338),
+ Complex::new(0.3172079107492197, 0.0),
+ Complex::new(0.32921644023662, -0.007126437606288196),
+ Complex::new(0.32921644023662, 0.007126437606288196),
+ Complex::new(-0.0118257849810276, 0.331081527121443),
+ Complex::new(-0.0118257849810276, -0.331081527121443),
+ Complex::new(0.2696397551921902, -0.1264099652316646),
+ Complex::new(0.2696397551921902, 0.1264099652316646)
+ ],
+ [
+ Complex::new(0.1286451879244679, 0.321493235676669),
+ Complex::new(0.1286451879244679, -0.321493235676669),
+ Complex::new(0.3460815928574744, 0.0),
+ Complex::new(0.2469587933800816, 0.2390686775345879),
+ Complex::new(0.2469587933800816, -0.2390686775345879),
+ Complex::new(-0.2911307584936315, -0.1361097216953978),
+ Complex::new(-0.2911307584936315, 0.1361097216953978),
+ Complex::new(0.1358210013533483, 0.2225358503734635),
+ Complex::new(0.1358210013533483, -0.2225358503734635)
+ ],
+ [
+ Complex::new(-0.2623103436214063, -0.2665640972326937),
+ Complex::new(-0.2623103436214063, 0.2665640972326937),
+ Complex::new(0.3775834865904633, 0.0),
+ Complex::new(0.003882464699551169, 0.3587545395223241),
+ Complex::new(0.003882464699551169, -0.3587545395223241),
+ Complex::new(0.2319301681252825, -0.2083294078640683),
+ Complex::new(0.2319301681252825, 0.2083294078640683),
+ Complex::new(-0.1806449439891257, 0.1395000635337823),
+ Complex::new(-0.1806449439891257, -0.1395000635337823)
+ ],
+ [
+ Complex::new(0.3725357186805364, 0.1560674488075285),
+ Complex::new(0.3725357186805364, -0.1560674488075285),
+ Complex::new(0.4119528235196378, 0.0),
+ Complex::new(-0.2633691995976155, 0.2662348200776662),
+ Complex::new(-0.2633691995976155, -0.2662348200776662),
+ Complex::new(0.098061488182526, 0.2860866634226474),
+ Complex::new(0.098061488182526, -0.2860866634226474),
+ Complex::new(-0.1386903372320934, -0.1438392442839339),
+ Complex::new(-0.1386903372320934, 0.1438392442839339)
+ ],
+ [
+ Complex::new(-0.4362230308412252,0.0),
+ Complex::new(-0.4362230308412252, 0.0),
+ Complex::new(0.4494506111435653, 0.0),
+ Complex::new(-0.3908970827922131, 0.0),
+ Complex::new(-0.3908970827922131, 0.0),
+ Complex::new(-0.2926266452874237, -0.02093116151478359),
+ Complex::new(-0.2926266452874237, 0.02093116151478359),
+ Complex::new(0.1118885064727042, -0.1344614042102196),
+ Complex::new(0.1118885064727042, 0.1344614042102196)
+ ],
+ [
+ Complex::new(0.2355352724921288, 0.007580122081110474),
+ Complex::new(0.2355352724921288, -0.007580122081110474),
+ Complex::new(0.2238752293291331, 0.0),
+ Complex::new(-0.2771386208238903, 0.01200387786755439),
+ Complex::new(-0.2771386208238903, -0.01200387786755439),
+ Complex::new(0.3741119620297071, 0.0),
+ Complex::new(0.3741119620297071, 0.0),
+ Complex::new(0.5069878940968082, 0.0),
+ Complex::new(0.5069878940968082, 0.0)
+ ],
+ [
+ Complex::new(-0.2377871721688999, 0.09074086536162872),
+ Complex::new(-0.2377871721688999, -0.09074086536162872),
+ Complex::new(0.2442533535325755, 0.0),
+ Complex::new(-0.2123489954513499, -0.1968431230236828),
+ Complex::new(-0.2123489954513499, 0.1968431230236828),
+ Complex::new(-0.1418684863318672, 0.3340361706185666),
+ Complex::new(-0.1418684863318672, -0.3340361706185666),
+ Complex::new(0.04854635659177065, 0.4411802158253109),
+ Complex::new(0.04854635659177065, -0.4411802158253109)
+ ]
+ ]}}
+}
+
+fn get_progress_matrix_inverse_eigenvectors() -> Matrix<Complex,9,9> {
+ Matrix { storage : { [
+ [
+ Complex::new(0.3486989487546765, 0.2989422454187725),
+ Complex::new(-0.1908373790664648, -0.380051505431422),
+ Complex::new(0.02700469819481069, 0.3928409356770213),
+ Complex::new(0.1174845534168364, -0.3451487519178137),
+ Complex::new(-0.2238159629573871, 0.2527255922474708),
+ Complex::new(0.281557071404708, -0.1357537764145929),
+ Complex::new(-0.2890191543835333, 0.01520148264425049),
+ Complex::new(0.2522618184392547, 0.09042023009009366),
+ Complex::new(-0.1830826837435938, -0.1674708088987285)
+ ],
+ [
+ Complex::new(0.3486989487546764, -0.2989422454187725),
+ Complex::new(-0.1908373790664648, 0.380051505431422),
+ Complex::new(0.02700469819481068, -0.3928409356770213),
+ Complex::new(0.1174845534168364, 0.3451487519178137),
+ Complex::new(-0.2238159629573871, -0.2527255922474708),
+ Complex::new(0.281557071404708, 0.1357537764145929),
+ Complex::new(-0.2890191543835333, -0.01520148264425048),
+ Complex::new(0.2522618184392547, -0.0904202300900937),
+ Complex::new(-0.1830826837435938, 0.1674708088987285)
+ ],
+ [
+ Complex::new(0.4742181559616455,0.0),
+ Complex::new(0.434654005287969, 0.0),
+ Complex::new(0.3983907025444076, 0.0),
+ Complex::new(0.3651528570838175, 0.0),
+ Complex::new(0.3346880541761948, 0.0),
+ Complex::new(0.3067649381216127, 0.0),
+ Complex::new(0.2811714552895747, 0.0),
+ Complex::new(0.2577132437420745, 0.0),
+ Complex::new(0.2362121572108401,0.0)
+ ],
+ [
+ Complex::new(-0.06569063068384375, 0.4095251163189331),
+ Complex::new(0.2346626794473929, 0.3206609638669744),
+ Complex::new(0.3765032858042049, 0.05622156347968291),
+ Complex::new(0.2919630559798539, -0.2185517880325913),
+ Complex::new(0.04785909443822407, -0.3461027137795469),
+ Complex::new(-0.2034806240242746, -0.2657849256929581),
+ Complex::new(-0.3181188512993283, -0.04048619576648209),
+ Complex::new(-0.2419090508777247, 0.1893887200088734),
+ Complex::new(-0.03399749420961479, 0.2923628066518291)
+ ],
+ [
+ Complex::new(-0.06569063068384372, -0.4095251163189331),
+ Complex::new(0.2346626794473929, -0.3206609638669743),
+ Complex::new(0.3765032858042049, -0.0562215634796829),
+ Complex::new(0.2919630559798539, 0.2185517880325913),
+ Complex::new(0.04785909443822408, 0.3461027137795469),
+ Complex::new(-0.2034806240242745, 0.2657849256929581),
+ Complex::new(-0.3181188512993282, 0.0404861957664821),
+ Complex::new(-0.2419090508777247, -0.1893887200088735),
+ Complex::new(-0.0339974942096148, -0.2923628066518291)
+ ],
+ [
+ Complex::new(-0.1566433899208904, 0.2940638465219954),
+ Complex::new(0.3421390950970197, 0.03012670869530254),
+ Complex::new(-0.109288840833082, -0.3367709716092889),
+ Complex::new(-0.2754964271512365, 0.2394067793128841),
+ Complex::new(0.3381739498974908, 0.1649231051921171),
+ Complex::new(0.02020740668890759, -0.3873284556685744),
+ Complex::new(-0.375650591965116, 0.1369107669482404),
+ Complex::new(0.281282749603226, 0.3012554655200025),
+ Complex::new(0.172488949725062, -0.3882872543576955)
+ ],
+ [
+ Complex::new(-0.1566433899208905, -0.2940638465219954),
+ Complex::new(0.3421390950970197, -0.03012670869530257),
+ Complex::new(-0.109288840833082, 0.3367709716092888),
+ Complex::new(-0.2754964271512365, -0.2394067793128841),
+ Complex::new(0.3381739498974908, -0.1649231051921171),
+ Complex::new(0.02020740668890758, 0.3873284556685744),
+ Complex::new(-0.375650591965116, -0.1369107669482404),
+ Complex::new(0.281282749603226, -0.3012554655200025),
+ Complex::new(0.172488949725062, 0.3882872543576955)
+ ],
+ [
+ Complex::new(-0.1728543990754797, -0.0951149567557127),
+ Complex::new(-0.1295910569370325, 0.1843779734304451),
+ Complex::new(0.1931546301457375, 0.1701754700451486),
+ Complex::new(0.217352157878041, -0.1980492340229828),
+ Complex::new(-0.1977124703087888, -0.2715287961454471),
+ Complex::new(-0.3329996849882462, 0.190561372632885),
+ Complex::new(0.1747618480311107, 0.4019012042245666),
+ Complex::new(0.4781590532795363, -0.1482144009366888),
+ Complex::new(-0.1085445157345817, -0.5614265629955691)
+ ],
+ [
+ Complex::new(-0.1728543990754797, 0.09511495675571273),
+ Complex::new(-0.1295910569370325, -0.1843779734304451),
+ Complex::new(0.1931546301457375, -0.1701754700451486),
+ Complex::new(0.2173521578780411, 0.1980492340229828),
+ Complex::new(-0.1977124703087888, 0.2715287961454471),
+ Complex::new(-0.3329996849882462, -0.1905613726328849),
+ Complex::new(0.1747618480311107, -0.4019012042245666),
+ Complex::new(0.4781590532795364, 0.1482144009366888),
+ Complex::new(-0.1085445157345817, 0.561426562995569)
+ ]
+ ]}}
+}
+
+#[cfg(test)]
+mod eigenvector_tests{
+ use super::*;
+ #[test]
+ fn test_inverse() {
+ let a = get_progress_matrix_inverse_eigenvectors();
+ let b = get_progress_matrix_eigenvectors();
+ let should_be_unit = a*b;
+ for (row, column) in (0..9).flat_map(|row| (0..9).map(move |column| (row, column))) {
+ assert!(should_be_unit[row][column].imag.abs() < 1e-10);
+ assert!((should_be_unit[row][column].real - if row == column {1.0} else { 0.0 }).abs() < 1e-10);
+ }
+ }
+}
+
+
+#[derive(Default, Debug, PartialEq, Copy, Clone)]
+struct Complex {
+ real : f64,
+ imag : f64
+}
+
+impl Add for Complex{
+ type Output = Complex;
+ fn add(self, rhs : Self) -> Self::Output {
+ Complex { real : self.real + rhs.real, imag : self.imag + rhs.imag }
+ }
+}
+
+impl Mul for Complex{
+ type Output = Complex;
+ fn mul(self, rhs : Self) -> Self::Output {
+ Complex {
+ real : self.real*rhs.real - self.imag*rhs.imag,
+ imag : self.real*rhs.imag + self.imag*rhs.real,
+ }
+ }
+}
+
+impl std::iter::Sum for Complex {
+ fn sum<I>(iter: I) -> Self
+ where I : Iterator<Item=Self>
+ {
+ iter.reduce(|lhs, rhs| lhs + rhs).unwrap_or_default()
+ }
+}
+
+impl Complex {
+ fn new(real : f64, imag : f64) -> Complex {
+ Complex { real, imag }
+ }
+ fn abs(self) -> f64 {
+ (self.real*self.real + self.imag*self.imag).sqrt()
+ }
+ fn powi(self, exponent : i32) -> Complex {
+ //this is waaaay easier to pull of in polar coordinates.
+ let abs = self.abs();
+ let angle = self.imag.atan2(self.real);
+ let powered_abs = abs.powi(exponent);
+ let powered_angle = exponent as f64 * angle;
+ let (s,c) = powered_angle.sin_cos();
+ Complex { real : powered_abs * c, imag : powered_abs * s }
+ }
+}
+
+#[cfg(test)]
+mod complex_tests{
+ use super::*;
+ #[test]
+ fn test_complex_mul() {
+ let a = Complex::new(3.4,7.2);
+ let b = Complex::new(0.3,4.6);
+ assert_eq!(a*b, b*a);
+ assert!(((a*b).real + 32.1).abs() < 1e-10 );
+ assert!(((a*b).imag -17.8).abs() < 1e-10 );
+ }
+
+ #[test]
+ fn test_complex_add() {
+ let a = Complex::new(3.4,7.2);
+ let b = Complex::new(0.3,4.6);
+ assert_eq!(a+b, b+a);
+ assert_eq!((a+b).real, 3.4+0.3);
+ assert_eq!((a+b).imag, 7.2+4.6);
+ }
+
+ #[test]
+ fn test_complex_powi() {
+ let c = Complex::new(0.78,1.2);
+ let d = c.powi(5);
+ assert!((1.5422086368 - d.real).abs() < 1e-10);
+ assert!((5.80392864 + d.imag).abs() < 1e-10);
+ }
+}
+//------------------------------------------------------------
+//Now we have all numbers ready, we can actually start implementing the closed form. Took long
+//enough..
+fn get_fish_count_after_days_using_diagonal_matrices(input : Matrix<Complex, 1, 9>, days : i32) -> Complex {
+ let c1 = Complex::new(1.0,0.0);
+ let sum_up : Matrix<Complex,9,1>
+ = Matrix { storage : [[c1,c1,c1,c1,c1,c1,c1,c1,c1]] };
+
+ let convert_to_diagonal_space = get_progress_matrix_inverse_eigenvectors();
+ let convert_back_to_fish_space = get_progress_matrix_eigenvectors();
+
+ let progress_per_day = get_progress_matrix_eigenvalues();
+
+ let progress_after_days = progress_per_day.map(|c| c.powi(days));
+
+ let progress_after_days = diag_matrix(progress_after_days);
+
+ (sum_up * convert_back_to_fish_space * progress_after_days * convert_to_diagonal_space * input)[0][0]
+}
+
+
+
+#[aoc(day6, part1, ClosedForm)]
+fn solve_part1_closed_form(input : &Fishtank) -> f64 {
+ //this is ugly, but I don't have the concentration today to make this better...
+ let adults = &input.adults;
+ let babies = &input.babies;
+ let initial_state : Matrix<Complex, 1, 9>
+ = Matrix { storage : [
+ [Complex::new(adults[0] as f64, 0.0)],
+ [Complex::new(adults[1] as f64, 0.0)],
+ [Complex::new(adults[2] as f64, 0.0)],
+ [Complex::new(adults[3] as f64, 0.0)],
+ [Complex::new(adults[4] as f64, 0.0)],
+ [Complex::new(adults[5] as f64, 0.0)],
+ [Complex::new(adults[6] as f64, 0.0)],
+ [Complex::new(babies[0] as f64, 0.0)],
+ [Complex::new(babies[1] as f64, 0.0)]
+ ] };
+
+ let fish_count = get_fish_count_after_days_using_diagonal_matrices(initial_state, PART1COUNT as i32);
+ fish_count.real.round()
+}
+
+#[aoc(day6, part2, ClosedForm)]
+fn solve_part2_closed_form(input : &Fishtank) -> f64 {
+ //this is ugly, but I don't have the concentration today to make this better...
+ let adults = &input.adults;
+ let babies = &input.babies;
+ let initial_state : Matrix<Complex, 1, 9>
+ = Matrix { storage : [
+ [Complex::new(adults[0] as f64, 0.0)],
+ [Complex::new(adults[1] as f64, 0.0)],
+ [Complex::new(adults[2] as f64, 0.0)],
+ [Complex::new(adults[3] as f64, 0.0)],
+ [Complex::new(adults[4] as f64, 0.0)],
+ [Complex::new(adults[5] as f64, 0.0)],
+ [Complex::new(adults[6] as f64, 0.0)],
+ [Complex::new(babies[0] as f64, 0.0)],
+ [Complex::new(babies[1] as f64, 0.0)]
+ ] };
+
+
+ let fish_count = get_fish_count_after_days_using_diagonal_matrices(initial_state, PART2COUNT as i32);
+ fish_count.real.round()
+}
+
+#[cfg(test)]
+mod day6_tests{
+ use super::*;
+
+ fn get_day6_string_testdata() -> &'static str { r#"3,4,3,1,2"# }
+
+ #[test]
+ fn test_day6_generator(){
+ let fishtank = input_generator(get_day6_string_testdata()).unwrap();
+ assert!(fishtank.adults.iter().eq([0,1,1,2,1,0,0].iter()));
+ assert_eq!(fishtank.count(), 5);
+ }
+
+ #[test]
+ fn test_day6_part1_fishtank() {
+ let fishtank = input_generator(get_day6_string_testdata()).unwrap();
+ let count = solve_day6_part1_fishtank(&fishtank);
+ assert_eq!(count, 5934);
+ }
+
+ #[test]
+ fn test_day6_part2_fishtank() {
+ let fishtank = input_generator(get_day6_string_testdata()).unwrap();
+ let count = solve_day6_part2_fishtank(&fishtank);
+ assert_eq!(count,26984457539);
+ }
+
+ #[test]
+ fn test_day6_part1_matrices() {
+ let fishtank = input_generator(get_day6_string_testdata()).unwrap();
+ let count = solve_part1_matrices(&fishtank);
+ assert_eq!(count, 5934);
+ }
+
+ #[test]
+ fn test_day6_part2_matrices() {
+ let fishtank = input_generator(get_day6_string_testdata()).unwrap();
+ let count = solve_part2_matrices(&fishtank);
+ assert_eq!(count,26984457539);
+ }
+
+ #[test]
+ fn test_day6_part1_closed_solution() {
+ let fishtank = input_generator(get_day6_string_testdata()).unwrap();
+ let count = solve_part1_closed_form(&fishtank);
+ assert_eq!(count as usize, 5934);
+ }
+
+ #[test]
+ fn test_day6_part2_closed_solution() {
+ let fishtank = input_generator(get_day6_string_testdata()).unwrap();
+ let count = solve_part2_closed_form(&fishtank);
+ assert_eq!(count as usize,26984457539);
+ }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 66130bd..a558ff4 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -5,5 +5,6 @@ pub mod day2;
pub mod day3;
pub mod day4;
pub mod day5;
+pub mod day6;
aoc_lib!{ year = 2021 }
diff --git a/src/ring_buffer.rs b/src/ring_buffer.rs
new file mode 100644
index 0000000..b523c9a
--- /dev/null
+++ b/src/ring_buffer.rs
@@ -0,0 +1,121 @@
+use std::fmt::{Display, Formatter};
+use std::error::Error;
+use std::ops::{Index, IndexMut};
+use std::borrow::Borrow;
+use std::iter::Iterator;
+
+pub struct RingBuffer<T, const SIZE : usize>
+{
+ //storage is an array, because arrays _guarantee_ an initialized fixed size.
+ //It's inside a box, because that way we can collect() into it without copying from heap to
+ //stack.
+ storage : Box<[T;SIZE]>,
+ index : usize,
+}
+
+#[derive(Debug)]
+pub struct RingBufferInitError;
+impl Display for RingBufferInitError {
+ fn fmt(&self, f: &mut Formatter) -> Result<(), std::fmt::Error> {
+ write!(f,"Not enough input to initialize the ring buffer")
+ }
+}
+impl Error for RingBufferInitError {}
+
+impl<T, const SIZE : usize> RingBuffer<T, SIZE>
+{
+ pub fn push_pop(mut self,new_entry : T) -> (Self, T) {
+ let old_value = std::mem::replace(&mut self.storage[self.index], new_entry);
+ self.index = (self.index + 1) % SIZE;
+ (self, old_value)
+ }
+
+ pub fn new<I>(mut input : I) -> Result<(Self,I), RingBufferInitError>
+ where I : Iterator<Item=T>
+ {
+ let slice = input.by_ref().take(SIZE).collect::<Box<[T]>>();
+ let array = slice.try_into().map_err(|_| RingBufferInitError)?;
+ Ok((RingBuffer { storage : array, index : 0 },input))
+ }
+
+ pub fn new_init(init : T) -> Self
+ where T : Copy
+ {
+ RingBuffer { storage : Box::new([init;SIZE]), index : 0 }
+ }
+
+ pub fn get(&self, index : usize) -> Option<&T> {
+ if index >= SIZE {
+ None
+ }
+ else {
+ Some(&self.storage[self.get_arr_index_wrapped(index)])
+ }
+ }
+
+ pub fn get_mut(&mut self, index: usize) -> Option<&mut T> {
+ if index >= SIZE {
+ None
+ }
+ else {
+ Some(&mut self.storage[self.get_arr_index_wrapped(index)])
+ }
+ }
+
+ pub fn iter(&self) -> RingBufferIterator<T,SIZE> {
+ RingBufferIterator { ring_buffer : self, index : 0 }
+ }
+
+ fn get_arr_index_wrapped(&self, index : usize) -> usize {
+ (self.index + index) % SIZE
+ }
+}
+
+impl<T, const SIZE :usize> Index<usize> for RingBuffer<T, SIZE> {
+ type Output = T;
+ fn index(&self, index : usize) -> &T {
+ self.get(index).expect("Ring buffer index out of bounds")
+ }
+}
+
+impl<T, const SIZE : usize> IndexMut<usize> for RingBuffer<T, SIZE> {
+ fn index_mut(&mut self, index :usize) -> &mut T {
+ self.get_mut(index).expect("Ring buffer index out of bounds")
+ }
+}
+
+pub struct RingBufferIterator<'a, T, const SIZE :usize> {
+ ring_buffer : &'a RingBuffer<T, SIZE>,
+ index : usize,
+}
+
+impl<'b, T, const SIZE : usize> Iterator for RingBufferIterator<'b, T, SIZE> {
+ type Item = &'b T;
+ fn next(&mut self) -> Option<&'b T> {
+ let result = self.ring_buffer.get(self.index);
+ if result.is_some() {
+ self.index += 1;
+ }
+ result
+ }
+}
+
+impl<T, const SIZE : usize> IntoIterator for RingBuffer<T, SIZE> {
+ type Item = T;
+ type IntoIter = std::collections::vec_deque::IntoIter<T>;
+ fn into_iter(self) -> Self::IntoIter {
+ let slice = self.storage as Box<[T]>;
+ let vec : Vec<T> = slice.into();
+ let mut deque : std::collections::VecDeque<T> = vec.into();
+ deque.rotate_left(self.index);
+ deque.into_iter()
+ }
+}
+
+impl<'b, T, const SIZE : usize> IntoIterator for &'b RingBuffer<T, SIZE> {
+ type Item = &'b T;
+ type IntoIter = RingBufferIterator<'b, T, SIZE>;
+ fn into_iter(self) -> Self::IntoIter {
+ self.iter()
+ }
+}