use crate::concepts::DigitOperate;
use crate::{Digit, Ternary};
use alloc::vec;
use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Sub, Shl, Shr};
impl Neg for &Ternary {
type Output = Ternary;
fn neg(self) -> Self::Output {
let mut repr = Ternary::new(vec![]);
for digit in self.digits.iter() {
repr.digits.push(-*digit);
}
repr
}
}
impl Add<&Ternary> for &Ternary {
type Output = Ternary;
fn add(self, rhs: &Ternary) -> Self::Output {
Ternary::from_dec(
self.to_dec()
.checked_add(rhs.to_dec())
.expect("Overflow in addition."),
)
}
}
impl Add<Digit> for &Ternary {
type Output = Ternary;
fn add(self, rhs: Digit) -> Self::Output {
Ternary::from_dec(
self.to_dec()
.checked_add(rhs.to_i8() as i64)
.expect("Overflow in addition."),
)
}
}
impl Sub<&Ternary> for &Ternary {
type Output = Ternary;
fn sub(self, rhs: &Ternary) -> Self::Output {
Ternary::from_dec(
self.to_dec()
.checked_sub(rhs.to_dec())
.expect("Overflow in subtraction."),
)
}
}
impl Sub<Digit> for &Ternary {
type Output = Ternary;
fn sub(self, rhs: Digit) -> Self::Output {
Ternary::from_dec(
self.to_dec()
.checked_sub(rhs.to_i8() as i64)
.expect("Overflow in subtraction."),
)
}
}
impl Mul<&Ternary> for &Ternary {
type Output = Ternary;
fn mul(self, rhs: &Ternary) -> Self::Output {
Ternary::from_dec(
self.to_dec()
.checked_mul(rhs.to_dec())
.expect("Overflow in multiplication."),
)
}
}
impl Div<&Ternary> for &Ternary {
type Output = Ternary;
fn div(self, rhs: &Ternary) -> Self::Output {
Ternary::from_dec(
self.to_dec()
.checked_div(rhs.to_dec())
.expect("Overflow in division or division by zero."),
)
}
}
impl BitAnd<&Ternary> for &Ternary {
type Output = Ternary;
fn bitand(self, rhs: &Ternary) -> Self::Output {
self.each_zip(Digit::bitand, rhs.clone())
}
}
impl BitOr<&Ternary> for &Ternary {
type Output = Ternary;
fn bitor(self, rhs: &Ternary) -> Self::Output {
self.each_zip(Digit::bitor, rhs.clone())
}
}
impl BitXor<&Ternary> for &Ternary {
type Output = Ternary;
fn bitxor(self, rhs: &Ternary) -> Self::Output {
self.each_zip(Digit::bitxor, rhs.clone())
}
}
impl Shl<usize> for &Ternary {
type Output = Ternary;
fn shl(self, rhs: usize) -> Self::Output {
let mut repr = Ternary::new(vec![]);
repr.digits.extend(self.digits.iter().cloned());
repr.digits.extend(core::iter::repeat(Digit::Zero).take(rhs));
repr
}
}
impl Shr<usize> for &Ternary {
type Output = Ternary;
fn shr(self, rhs: usize) -> Self::Output {
if rhs >= self.digits.len() {
return Ternary::parse("0");
}
let len = self.digits.len() - rhs;
let mut repr = Ternary::new(self.digits[..len].to_vec());
if repr.digits.is_empty() {
repr.digits.push(Digit::Zero);
}
repr
}
}
impl Not for &Ternary {
type Output = Ternary;
fn not(self) -> Self::Output {
-self
}
}
#[cfg(test)]
#[test]
fn test_ternary_ops() {
use alloc::string::ToString;
let repr9 = Ternary::parse("+00");
let repr4 = Ternary::parse("++");
let repr13 = &repr9 + &repr4;
let repr17 = &repr13 + &repr4;
let repr34 = &repr17 + &repr17;
assert_eq!(repr13.to_string(), "+++");
assert_eq!(repr17.to_string(), "+-0-");
assert_eq!(repr34.to_string(), "++-+");
let repr30 = &repr34 - &repr4;
assert_eq!(repr30.to_dec(), 30);
assert_eq!(repr30.to_string(), "+0+0");
let repr120 = &repr30 * &repr4;
assert_eq!(repr120.to_dec(), 120);
assert_eq!(repr120.to_string(), "++++0");
let repr_neg120 = -&repr120;
assert_eq!(repr_neg120.to_dec(), -120);
assert_eq!(repr_neg120.to_string(), "----0");
let bitwise = &Ternary::parse("++00") & &Ternary::parse("0000");
assert_eq!(bitwise.to_string(), "0000");
let bitwise = &Ternary::parse("++00") & &Ternary::parse("0+00");
assert_eq!(bitwise.to_string(), "0+00");
let bitwise = &Ternary::parse("+000") | &Ternary::parse("000-");
assert_eq!(bitwise.to_string(), "+000");
let bitwise = &Ternary::parse("+000") & &Ternary::parse("000-");
assert_eq!(bitwise.to_string(), "000-");
let bitwise = &Ternary::parse("+000") | &Ternary::parse("000+");
assert_eq!(bitwise.to_string(), "+00+");
}
#[cfg(test)]
#[test]
fn test_shift_ops() {
use alloc::string::ToString;
let t = Ternary::parse("+0-");
assert_eq!((&t << 2).to_string(), "+0-00");
let back = &(&t << 2) >> 2;
assert_eq!(back.to_string(), "+0-");
let zero = &t >> 5;
assert_eq!(zero.to_string(), "0");
}