use std::{ cmp::min, convert::{TryFrom, TryInto}, fmt, ops::{Add, Mul, Neg}, str::FromStr, }; fn main() -> Result<(), &'static str> { let a = BalancedTernary::from_str("+-0++0+")?; let b = BalancedTernary::from(-436); let c = BalancedTernary::from_str("+-++-")?; println!("a = {} = {}", a, i128::try_from(a.clone())?); println!("b = {} = {}", b, i128::try_from(b.clone())?); println!("c = {} = {}", c, i128::try_from(c.clone())?); let d = a * (b + -c); println!("a * (b - c) = {} = {}", d, i128::try_from(d.clone())?); let e = BalancedTernary::from_str( "+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++", )?; assert_eq!(i128::try_from(e).is_err(), true); Ok(()) } #[derive(Clone, Copy, PartialEq)] enum Trit { Zero, Pos, Neg, } impl TryFrom for Trit { type Error = &'static str; fn try_from(value: char) -> Result { match value { '0' => Ok(Self::Zero), '+' => Ok(Self::Pos), '-' => Ok(Self::Neg), _ => Err("Invalid character for balanced ternary"), } } } impl From for char { fn from(x: Trit) -> Self { match x { Trit::Zero => '0', Trit::Pos => '+', Trit::Neg => '-', } } } impl Add for Trit { // (Carry, Current) type Output = (Self, Self); fn add(self, rhs: Self) -> Self::Output { use Trit::{Neg, Pos, Zero}; match (self, rhs) { (Zero, x) | (x, Zero) => (Zero, x), (Pos, Neg) | (Neg, Pos) => (Zero, Zero), (Pos, Pos) => (Pos, Neg), (Neg, Neg) => (Neg, Pos), } } } impl Mul for Trit { type Output = Self; fn mul(self, rhs: Self) -> Self::Output { use Trit::{Neg, Pos, Zero}; match (self, rhs) { (Zero, _) | (_, Zero) => Zero, (Pos, Pos) | (Neg, Neg) => Pos, (Pos, Neg) | (Neg, Pos) => Neg, } } } impl Neg for Trit { type Output = Self; fn neg(self) -> Self::Output { match self { Trit::Zero => Trit::Zero, Trit::Pos => Trit::Neg, Trit::Neg => Trit::Pos, } } } // The vector is stored in reverse from how it would be viewed, as // operations tend to work backwards #[derive(Clone)] struct BalancedTernary(Vec); impl fmt::Display for BalancedTernary { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{}", self.0 .iter() .rev() .map(|&d| char::from(d)) .collect::() ) } } impl Add for BalancedTernary { type Output = Self; fn add(self, rhs: Self) -> Self::Output { use Trit::Zero; // Trim leading zeroes fn trim(v: &mut Vec) { while let Some(last_elem) = v.pop() { if last_elem != Zero { v.push(last_elem); break; } } } if rhs.0.is_empty() { // A balanced ternary shouldn't be empty if self.0.is_empty() { return BalancedTernary(vec![Zero]); } return self; } let length = min(self.0.len(), rhs.0.len()); let mut sum = Vec::new(); let mut carry = vec![Zero]; for i in 0..length { let (carry_dig, digit) = self.0[i] + rhs.0[i]; sum.push(digit); carry.push(carry_dig); } // At least one of these two loops will be ignored for i in length..self.0.len() { sum.push(self.0[i]); } for i in length..rhs.0.len() { sum.push(rhs.0[i]); } trim(&mut sum); trim(&mut carry); BalancedTernary(sum) + BalancedTernary(carry) } } // This version of `Mul` requires an implementation of the `Add` trait impl Mul for BalancedTernary { type Output = Self; fn mul(self, rhs: Self) -> Self::Output { let mut results = Vec::with_capacity(rhs.0.len()); for i in 0..rhs.0.len() { let mut digits = vec![Trit::Zero; i]; for j in 0..self.0.len() { digits.push(self.0[j] * rhs.0[i]); } results.push(BalancedTernary(digits)); } #[allow(clippy::suspicious_arithmetic_impl)] results .into_iter() .fold(BalancedTernary(vec![Trit::Zero]), |acc, x| acc + x) } } impl Neg for BalancedTernary { type Output = Self; fn neg(self) -> Self::Output { BalancedTernary(self.0.iter().map(|&x| -x).collect()) } } impl FromStr for BalancedTernary { type Err = &'static str; fn from_str(s: &str) -> Result { s.chars() .rev() .map(|c| c.try_into()) .collect::>() .map(BalancedTernary) } } impl From for BalancedTernary { fn from(x: i128) -> Self { let mut v = Vec::new(); let mut curr = x; loop { let rem = curr % 3; match rem { 0 => v.push(Trit::Zero), 1 | -2 => v.push(Trit::Pos), 2 | -1 => v.push(Trit::Neg), _ => unreachable!(), } let offset = (rem as f64 / 3.0).round() as i128; curr = curr / 3 + offset; if curr == 0 { break; } } BalancedTernary(v) } } impl TryFrom for i128 { type Error = &'static str; fn try_from(value: BalancedTernary) -> Result { value .0 .iter() .enumerate() .try_fold(0_i128, |acc, (i, character)| { let size_err = "Balanced ternary string is too large to fit into 16 bytes"; let index: u32 = i.try_into().map_err(|_| size_err)?; match character { Trit::Zero => Ok(acc), Trit::Pos => 3_i128 .checked_pow(index) .and_then(|x| acc.checked_add(x)) .ok_or(size_err), Trit::Neg => 3_i128 .checked_pow(index) .and_then(|x| acc.checked_sub(x)) .ok_or(size_err), } }) } }