balanced_ternary/
operations.rs

1//! This module provides implementations for arithmetic operations on the `Ternary` type
2//! such as addition, subtraction, multiplication, and division.
3//! Using `Ternary` arithmetic:
4//!
5//! ```rust
6//! use balanced_ternary::Ternary;
7//!
8//! let repr9 = Ternary::parse("+00"); // Represents decimal 9 in balanced ternary
9//! let repr4 = Ternary::parse("++");  // Represents decimal 4 in balanced ternary
10//! let sum = &repr9 + &repr4;         // Results in Ternary::parse("+++"), decimal 13
11//! assert_eq!(sum.to_dec(), 13);
12//! let difference = &sum - &repr4;   // Results in Ternary::parse("+00"), decimal 9
13//! assert_eq!(difference.to_dec(), 9);
14//! ```
15//!
16//! # Implementations
17//!
18//! The following arithmetic operations are implemented for the `Ternary` :
19//!
20//! ## `Ternary` type
21//!
22//! - `Neg` and `Not` for `&Ternary`: Negates the `Ternary` by negating each digit in its balanced ternary representation.
23//! - `Add<&Ternary>` for `&Ternary`: Adds two `Ternary` values and returns a new `Ternary`. Panics on overflow.
24//! - `Sub<&Ternary>` for `&Ternary`: Subtracts one `Ternary` from another and returns a new `Ternary`. Panics on overflow.
25//! - `Mul<&Ternary>` for `&Ternary`: Multiplies two `Ternary` values and returns a new `Ternary`. Panics on overflow.
26//! - `Div<&Ternary>` for `&Ternary`: Divides one `Ternary` by another and returns a new `Ternary`. Panics on overflow or division by zero.
27//! - `BitAnd<&Ternary>` for `&Ternary`: Computes the bitwise AND operation on two `Ternary` operands.
28//! - `BitOr<&Ternary>` for `&Ternary`: Computes the bitwise OR operation on two `Ternary` operands.
29//! - `BitXor<&Ternary>` for `&Ternary`: Computes the bitwise XOR operation on two `Ternary` operands.
30
31use crate::concepts::DigitOperate;
32use crate::{Digit, Ternary};
33use alloc::vec;
34use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Sub, Shl, Shr};
35
36impl Neg for &Ternary {
37    type Output = Ternary;
38
39    /// Returns the negation of the current `Ternary` object.
40    ///
41    /// Negates each digit in the number.
42    fn neg(self) -> Self::Output {
43        let mut repr = Ternary::new(vec![]);
44        for digit in self.digits.iter() {
45            repr.digits.push(-*digit);
46        }
47        repr
48    }
49}
50
51impl Add<&Ternary> for &Ternary {
52    type Output = Ternary;
53
54    fn add(self, rhs: &Ternary) -> Self::Output {
55        Ternary::from_dec(
56            self.to_dec()
57                .checked_add(rhs.to_dec())
58                .expect("Overflow in addition."),
59        )
60    }
61}
62
63impl Add<Digit> for &Ternary {
64    type Output = Ternary;
65
66    fn add(self, rhs: Digit) -> Self::Output {
67        Ternary::from_dec(
68            self.to_dec()
69                .checked_add(rhs.to_i8() as i64)
70                .expect("Overflow in addition."),
71        )
72    }
73}
74
75impl Sub<&Ternary> for &Ternary {
76    type Output = Ternary;
77
78    fn sub(self, rhs: &Ternary) -> Self::Output {
79        Ternary::from_dec(
80            self.to_dec()
81                .checked_sub(rhs.to_dec())
82                .expect("Overflow in subtraction."),
83        )
84    }
85}
86
87impl Sub<Digit> for &Ternary {
88    type Output = Ternary;
89    fn sub(self, rhs: Digit) -> Self::Output {
90        Ternary::from_dec(
91            self.to_dec()
92                .checked_sub(rhs.to_i8() as i64)
93                .expect("Overflow in subtraction."),
94        )
95    }
96}
97
98impl Mul<&Ternary> for &Ternary {
99    type Output = Ternary;
100
101    fn mul(self, rhs: &Ternary) -> Self::Output {
102        Ternary::from_dec(
103            self.to_dec()
104                .checked_mul(rhs.to_dec())
105                .expect("Overflow in multiplication."),
106        )
107    }
108}
109
110impl Div<&Ternary> for &Ternary {
111    type Output = Ternary;
112
113    fn div(self, rhs: &Ternary) -> Self::Output {
114        Ternary::from_dec(
115            self.to_dec()
116                .checked_div(rhs.to_dec())
117                .expect("Overflow in division or division by zero."),
118        )
119    }
120}
121
122impl BitAnd<&Ternary> for &Ternary {
123    type Output = Ternary;
124
125    fn bitand(self, rhs: &Ternary) -> Self::Output {
126        self.each_zip(Digit::bitand, rhs.clone())
127    }
128}
129
130impl BitOr<&Ternary> for &Ternary {
131    type Output = Ternary;
132
133    fn bitor(self, rhs: &Ternary) -> Self::Output {
134        self.each_zip(Digit::bitor, rhs.clone())
135    }
136}
137
138impl BitXor<&Ternary> for &Ternary {
139    type Output = Ternary;
140
141    fn bitxor(self, rhs: &Ternary) -> Self::Output {
142        self.each_zip(Digit::bitxor, rhs.clone())
143    }
144}
145
146impl Shl<usize> for &Ternary {
147    type Output = Ternary;
148
149    fn shl(self, rhs: usize) -> Self::Output {
150        let mut repr = Ternary::new(vec![]);
151        repr.digits.extend(self.digits.iter().cloned());
152        repr.digits.extend(core::iter::repeat(Digit::Zero).take(rhs));
153        repr
154    }
155}
156
157impl Shr<usize> for &Ternary {
158    type Output = Ternary;
159
160    fn shr(self, rhs: usize) -> Self::Output {
161        if rhs >= self.digits.len() {
162            return Ternary::parse("0");
163        }
164        let len = self.digits.len() - rhs;
165        let mut repr = Ternary::new(self.digits[..len].to_vec());
166        if repr.digits.is_empty() {
167            repr.digits.push(Digit::Zero);
168        }
169        repr
170    }
171}
172
173impl Not for &Ternary {
174    type Output = Ternary;
175    fn not(self) -> Self::Output {
176        -self
177    }
178}
179
180#[cfg(test)]
181#[test]
182fn test_ternary_ops() {
183    use alloc::string::ToString;
184
185    let repr9 = Ternary::parse("+00");
186    let repr4 = Ternary::parse("++");
187    let repr13 = &repr9 + &repr4;
188    let repr17 = &repr13 + &repr4;
189    let repr34 = &repr17 + &repr17;
190
191    assert_eq!(repr13.to_string(), "+++");
192    assert_eq!(repr17.to_string(), "+-0-");
193    assert_eq!(repr34.to_string(), "++-+");
194
195    let repr30 = &repr34 - &repr4;
196    assert_eq!(repr30.to_dec(), 30);
197    assert_eq!(repr30.to_string(), "+0+0");
198
199    let repr120 = &repr30 * &repr4;
200    assert_eq!(repr120.to_dec(), 120);
201    assert_eq!(repr120.to_string(), "++++0");
202
203    let repr_neg120 = -&repr120;
204    assert_eq!(repr_neg120.to_dec(), -120);
205    assert_eq!(repr_neg120.to_string(), "----0");
206
207    let bitwise = &Ternary::parse("++00") & &Ternary::parse("0000");
208    assert_eq!(bitwise.to_string(), "0000");
209
210    let bitwise = &Ternary::parse("++00") & &Ternary::parse("0+00");
211    assert_eq!(bitwise.to_string(), "0+00");
212
213    let bitwise = &Ternary::parse("+000") | &Ternary::parse("000-");
214    assert_eq!(bitwise.to_string(), "+000");
215
216    let bitwise = &Ternary::parse("+000") & &Ternary::parse("000-");
217    assert_eq!(bitwise.to_string(), "000-");
218
219    let bitwise = &Ternary::parse("+000") | &Ternary::parse("000+");
220    assert_eq!(bitwise.to_string(), "+00+");
221}
222
223#[cfg(test)]
224#[test]
225fn test_shift_ops() {
226    use alloc::string::ToString;
227    let t = Ternary::parse("+0-");
228    assert_eq!((&t << 2).to_string(), "+0-00");
229    let back = &(&t << 2) >> 2;
230    assert_eq!(back.to_string(), "+0-");
231    let zero = &t >> 5;
232    assert_eq!(zero.to_string(), "0");
233}