259 lines
7.6 KiB
Rust
259 lines
7.6 KiB
Rust
use std::fmt::Debug;
|
|
use std::ops::Deref;
|
|
use std::{borrow::Borrow, ops::Add};
|
|
|
|
#[derive(Clone, PartialEq)]
|
|
pub enum SnailfishNumber {
|
|
Pair(Box<SnailfishNumber>, Box<SnailfishNumber>),
|
|
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<T> From<T> for SnailfishNumber
|
|
where
|
|
T: Borrow<Box<SnailfishNumber>>,
|
|
{
|
|
fn from(s: T) -> Self {
|
|
s.borrow().deref().clone()
|
|
}
|
|
}
|
|
|
|
impl From<isize> for SnailfishNumber {
|
|
fn from(s: isize) -> Self {
|
|
Self::Regular(s)
|
|
}
|
|
}
|
|
|
|
impl From<&isize> for SnailfishNumber {
|
|
fn from(s: &isize) -> Self {
|
|
Self::Regular(*s)
|
|
}
|
|
}
|
|
|
|
impl<LHS, RHS> From<(LHS, RHS)> for SnailfishNumber
|
|
where
|
|
LHS: Into<SnailfishNumber>,
|
|
RHS: Into<SnailfishNumber>,
|
|
{
|
|
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<Self::Item> {
|
|
// 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<isize> {
|
|
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<isize>, Self, Option<isize>, 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);
|
|
}
|
|
}
|