use std::fmt::Debug; use std::ops::Deref; use std::{borrow::Borrow, ops::Add}; #[derive(Clone, PartialEq)] pub enum SnailfishNumber { Pair(Box, Box), Regular(isize), } impl Debug for SnailfishNumber { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { SnailfishNumber::Pair(lhs, rhs) => { f.write_str("[")?; Debug::fmt(&lhs, f)?; f.write_str(",")?; Debug::fmt(&rhs, f)?; f.write_str("]")?; Ok(()) } SnailfishNumber::Regular(n) => Debug::fmt(n, f), } } } impl From for SnailfishNumber where T: Borrow>, { fn from(s: T) -> Self { s.borrow().deref().clone() } } impl From for SnailfishNumber { fn from(s: isize) -> Self { Self::Regular(s) } } impl From<&isize> for SnailfishNumber { fn from(s: &isize) -> Self { Self::Regular(*s) } } impl From<(LHS, RHS)> for SnailfishNumber where LHS: Into, RHS: Into, { fn from(s: (LHS, RHS)) -> Self { Self::Pair(Box::new(s.0.into()), Box::new(s.1.into())) } } //impl From<&SnailfishNumber> for SnailfishNumberIterator { // fn from(inner: &SnailfishNumber) -> Self { // SnailfishNumberIterator { inner: inner.clone(), index: 0 } // } //} // //pub struct SnailfishNumberIterator { // inner: SnailfishNumber, // index: usize, //} // //impl Iterator for SnailfishNumberIterator { // type Item = isize; // // fn next(&mut self) -> Option { // let next = match self.inner { // SnailfishNumber::Pair(rhs, lhs) => { // let rhs = SnailfishNumberIterator::from(rhs.deref()); // let lhs = SnailfishNumberIterator::from(lhs.deref()); // let chain = rhs.chain(lhs); // chain.nth(self.index) // }, // SnailfishNumber::Regular(n) => { // if self.index == 0 { // Some(n) // } else { // None // } // } // }; // if let Some(next) = next { // self.index += 1; // Some(next) // } else { // None // } // } //} impl SnailfishNumber { pub fn regular(&self) -> Option { match self { SnailfishNumber::Regular(n) => Some(*n), _ => None, } } pub fn magnitude(&self) -> isize { match self { SnailfishNumber::Pair(lhs, rhs) => 3 * lhs.magnitude() + 2 * rhs.magnitude(), SnailfishNumber::Regular(reg) => *reg, } } pub fn reduce(self) -> Self { let mut new = self; while let (changed, true) = new.reduce_once() { new = changed; } new } pub fn reduce_once(&self) -> (Self, bool) { if let (_, changed, _, true) = self.explode(0) { println!("after explode: {:?}", &changed); (changed, true) } else if let (changed, true) = self.split() { println!("after split: {:?}", &changed); (changed, true) } else { (self.clone(), false) } } pub fn split(&self) -> (Self, bool) { match self { Self::Regular(n) if n >= &10 => { let lhs = ((*n as f64) / 2.0).floor() as isize; let rhs = ((*n as f64) / 2.0).ceil() as isize; ((lhs, rhs).into(), true) } Self::Regular(n) => (n.into(), false), Self::Pair(lhs, rhs) => match lhs.split() { (lhs, true) => ((lhs, rhs).into(), true), (_, false) => match rhs.split() { (rhs, true) => (Self::Pair(lhs.clone(), Box::new(rhs)), true), (_, false) => (self.clone(), false), }, }, } } pub fn explode(&self, depth: usize) -> (Option, Self, Option, bool) { let new = self.clone(); match new { SnailfishNumber::Pair(mut lhs, mut rhs) => { if depth == 4 { let (lhs, rhs) = (lhs.regular().unwrap(), rhs.regular().unwrap()); (Some(lhs), 0.into(), Some(rhs), true) } else { let (left_addition, inner, right_addition, exploded) = lhs.explode(depth + 1); if exploded { if let Some(right_addition) = right_addition { rhs.add_right(right_addition); } return (left_addition, (inner, rhs).into(), None, true); } let (left_addition, inner, right_addition, exploded) = rhs.explode(depth + 1); if exploded { if let Some(left_addition) = left_addition { lhs.add_left(left_addition); } return (None, (lhs, inner).into(), right_addition, true); } (None, self.clone(), None, false) } } regular => (None, regular, None, false), } } fn add_left(&mut self, other: isize) { match self { Self::Pair(_, rhs) => rhs.add_left(other), Self::Regular(n) => *n += other, } } fn add_right(&mut self, other: isize) { match self { Self::Pair(lhs, _) => lhs.add_right(other), Self::Regular(n) => *n += other, } } } impl Add for SnailfishNumber { type Output = Self; fn add(self, rhs: Self) -> Self::Output { Self::from((self, rhs)).reduce() } } #[cfg(test)] mod test { use super::SnailfishNumber; #[test] fn multiple_reductions_example() { let lhs: SnailfishNumber = ((((4, 3), 4), 4), (7, ((8, 4), 9))).into(); let rhs: SnailfishNumber = (1, 1).into(); let result: SnailfishNumber = ((((0, 7), 4), ((7, 8), (6, 0))), (8, 1)).into(); assert_eq!(lhs + rhs, result); } #[test] fn explosion_1() { let number: SnailfishNumber = (((((9, 8), 1), 2), 3), 4).into(); let result: SnailfishNumber = ((((0, 9), 2), 3), 4).into(); assert_eq!(number.reduce(), result); } #[test] fn explosion_2() { let number: SnailfishNumber = (7, (6, (5, (4, (3, 2))))).into(); let result: SnailfishNumber = (7, (6, (5, (7, 0)))).into(); assert_eq!(number.reduce(), result); } #[test] fn explosion_3() { let number: SnailfishNumber = ((6, (5, (4, (3, 2)))), 1).into(); let result: SnailfishNumber = ((6, (5, (7, 0))), 3).into(); assert_eq!(number.reduce(), result); } #[test] fn explosion_4() { let number: SnailfishNumber = ((3, (2, (1, (7, 3)))), (6, (5, (4, (3, 2))))).into(); let result: SnailfishNumber = ((3, (2, (8, 0))), (9, (5, (4, (3, 2))))).into(); assert_eq!(number.reduce_once(), (result, true)); } #[test] fn explosion_5() { let number: SnailfishNumber = ((3, (2, (8, 0))), (9, (5, (4, (3, 2))))).into(); let result: SnailfishNumber = ((3, (2, (8, 0))), (9, (5, (7, 0)))).into(); assert_eq!(number.reduce(), result); } #[test] fn magnitude() { let number: SnailfishNumber = ( (((8, 7), (7, 7)), ((8, 6), (7, 7))), (((0, 7), (6, 6)), (8, 7)), ) .into(); assert_eq!(number.magnitude(), 3488); } }