balanced_ternary/
operations.rs

1//! This module provides implementations for arithmetic operations on `Digit` and `Ternary` types
2//! such as addition, subtraction, multiplication, and division.
3//!
4//! These operations adhere to the rules of balanced ternary arithmetic.
5//!
6//! # Examples
7//!
8//! Using `Digit` arithmetic:
9//!
10//! ```rust
11//! use balanced_ternary::Digit;
12//!
13//! let a = Digit::Neg;
14//! let b = Digit::Zero;
15//! let sum = a + b;
16//! assert_eq!(sum.to_string(), "-");
17//! let product = a * b; // Results in Digit::Neg
18//! assert_eq!(product.to_char(), '0')
19//! ```
20//!
21//! Using `Ternary` arithmetic:
22//!
23//! ```rust
24//! use balanced_ternary::Ternary;
25//!
26//! let repr9 = Ternary::parse("+00"); // Represents decimal 9 in balanced ternary
27//! let repr4 = Ternary::parse("++");  // Represents decimal 4 in balanced ternary
28//! let sum = &repr9 + &repr4;         // Results in Ternary::parse("+++"), decimal 13
29//! assert_eq!(sum.to_dec(), 13);
30//! let difference = &sum - &repr4;   // Results in Ternary::parse("+00"), decimal 9
31//! assert_eq!(difference.to_dec(), 9);
32//! ```
33//!
34//! # Implementations
35//!
36//! The following arithmetic operations are implemented for `Digit` and `Ternary` types:
37//!
38//! ## `Digit` type
39//!
40//! - `Neg` for `Digit`: Negates the digit value, adhering to balanced ternary rules.
41//! - `Add<Digit>` for `Digit`: Adds two `Digit` values and returns a `Ternary`.
42//! - `Sub<Digit>` for `Digit`: Subtracts one `Digit` from another and returns a `Ternary`.
43//! - `Mul<Digit>` for `Digit`: Multiplies two `Digit` values and returns a `Digit`.
44//! - `Div<Digit>` for `Digit`: Divides one `Digit` by another and returns a `Digit`. Division by zero panics.
45//!
46//! ### Logical Operations for `Digit`
47//!
48//! The `Digit` type supports bitwise logical operations, which are implemented according to logical rules applicable to balanced ternary digits.
49//!
50//! #### `BitAnd` for `Digit`
51//!
52//! Performs a bitwise AND operation between two `Digit` values.
53//!
54//! - `Digit::Neg & other` → `Digit::Neg`
55//! - `Digit::Zero & Digit::Neg` → `Digit::Neg`
56//! - `Digit::Zero & other` → `Digit::Zero`
57//! - `Digit::Pos & other` → `other`
58//!
59//! #### `BitOr` for `Digit`
60//!
61//! Performs a bitwise OR operation between two `Digit` values.
62//!
63//! - `Digit::Neg | other` → `other`
64//! - `Digit::Zero | Digit::Pos` → `Digit::Pos`
65//! - `Digit::Zero | other` → `Digit::Zero`
66//! - `Digit::Pos | other` → `Digit::Pos`
67//!
68//! #### `BitXor` for `Digit`
69//!
70//! Performs a bitwise XOR operation between two `Digit` values.
71//!
72//! - `Digit::Neg ^ other` → `other`
73//! - `Digit::Zero ^ other` → `Digit::Zero`
74//! - `Digit::Pos ^ other` → `-other`
75//!
76//! ## `Ternary` type
77//!
78//! - `Neg` for `&Ternary`: Negates the `Ternary` by negating each digit in its balanced ternary representation.
79//! - `Add<&Ternary>` for `&Ternary`: Adds two `Ternary` values and returns a new `Ternary`. Panics on overflow.
80//! - `Sub<&Ternary>` for `&Ternary`: Subtracts one `Ternary` from another and returns a new `Ternary`. Panics on overflow.
81//! - `Mul<&Ternary>` for `&Ternary`: Multiplies two `Ternary` values and returns a new `Ternary`. Panics on overflow.
82//! - `Div<&Ternary>` for `&Ternary`: Divides one `Ternary` by another and returns a new `Ternary`. Panics on overflow or division by zero.
83
84use crate::{Digit, Ternary};
85use alloc::vec;
86use alloc::vec::Vec;
87use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Sub};
88
89impl Neg for Digit {
90    type Output = Self;
91
92    /// Returns the negation of the `Digit`.
93    ///
94    /// - `Digit::Neg` becomes `Digit::Pos`
95    /// - `Digit::Pos` becomes `Digit::Neg`
96    /// - `Digit::Zero` remains `Digit::Zero`
97    fn neg(self) -> Self::Output {
98        match self {
99            Digit::Neg => Digit::Pos,
100            Digit::Zero => Digit::Zero,
101            Digit::Pos => Digit::Neg,
102        }
103    }
104}
105
106impl Add<Digit> for Digit {
107    type Output = Ternary;
108    fn add(self, other: Digit) -> Self::Output {
109        match self {
110            Digit::Neg => match other {
111                Digit::Neg => Ternary::parse("-+"),
112                Digit::Zero => Ternary::parse("-"),
113                Digit::Pos => Ternary::parse("0"),
114            },
115            Digit::Zero => Ternary::new(vec![other]),
116            Digit::Pos => match other {
117                Digit::Neg => Ternary::parse("0"),
118                Digit::Zero => Ternary::parse("+"),
119                Digit::Pos => Ternary::parse("+-"),
120            },
121        }
122    }
123}
124
125impl Sub<Digit> for Digit {
126    type Output = Ternary;
127    fn sub(self, other: Digit) -> Self::Output {
128        match self {
129            Digit::Neg => match other {
130                Digit::Neg => Ternary::parse("0"),
131                Digit::Zero => Ternary::parse("-"),
132                Digit::Pos => Ternary::parse("-+"),
133            },
134            Digit::Zero => Ternary::new(vec![-other]),
135            Digit::Pos => match other {
136                Digit::Neg => Ternary::parse("+-"),
137                Digit::Zero => Ternary::parse("+"),
138                Digit::Pos => Ternary::parse("0"),
139            },
140        }
141    }
142}
143
144impl Mul<Digit> for Digit {
145    type Output = Digit;
146
147    fn mul(self, other: Digit) -> Self::Output {
148        match self {
149            Digit::Neg => -other,
150            Digit::Zero => Digit::Zero,
151            Digit::Pos => other,
152        }
153    }
154}
155
156impl Div<Digit> for Digit {
157    type Output = Digit;
158
159    fn div(self, other: Digit) -> Self::Output {
160        match self {
161            Digit::Neg => match other {
162                Digit::Neg => Digit::Pos,
163                Digit::Zero => panic!("Cannot divide by zero."),
164                Digit::Pos => Digit::Neg,
165            },
166            Digit::Zero => match other {
167                Digit::Neg => Digit::Zero,
168                Digit::Zero => panic!("Cannot divide by zero."),
169                Digit::Pos => Digit::Zero,
170            },
171            Digit::Pos => match other {
172                Digit::Neg => Digit::Neg,
173                Digit::Zero => panic!("Cannot divide by zero."),
174                Digit::Pos => Digit::Pos,
175            },
176        }
177    }
178}
179
180impl BitAnd for Digit {
181    type Output = Self;
182    fn bitand(self, other: Self) -> Self::Output {
183        match self {
184            Digit::Neg => Digit::Neg,
185            Digit::Zero => match other {
186                Digit::Neg => Digit::Neg,
187                _ => Digit::Zero,
188            },
189            Digit::Pos => other,
190        }
191    }
192}
193
194impl BitOr for Digit {
195    type Output = Self;
196    fn bitor(self, other: Self) -> Self::Output {
197        match self {
198            Digit::Neg => other,
199            Digit::Zero => match other {
200                Digit::Pos => Digit::Pos,
201                _ => Digit::Zero,
202            },
203            Digit::Pos => Digit::Pos,
204        }
205    }
206}
207
208impl BitXor for Digit {
209    type Output = Self;
210
211    fn bitxor(self, rhs: Self) -> Self::Output {
212        match self {
213            Digit::Neg => rhs,
214            Digit::Zero => Digit::Zero,
215            Digit::Pos => -rhs,
216        }
217    }
218}
219
220impl Neg for &Ternary {
221    type Output = Ternary;
222
223    /// Returns the negation of the current `Ternary` object.
224    ///
225    /// Negates each digit in the number.
226    fn neg(self) -> Self::Output {
227        let mut repr = Ternary::new(vec![]);
228        for digit in self.digits.iter() {
229            repr.digits.push(-*digit);
230        }
231        repr
232    }
233}
234
235impl Add<&Ternary> for &Ternary {
236    type Output = Ternary;
237
238    fn add(self, rhs: &Ternary) -> Self::Output {
239        Ternary::from_dec(
240            self.to_dec()
241                .checked_add(rhs.to_dec())
242                .expect("Overflow in addition."),
243        )
244    }
245}
246
247impl Sub<&Ternary> for &Ternary {
248    type Output = Ternary;
249
250    fn sub(self, rhs: &Ternary) -> Self::Output {
251        Ternary::from_dec(
252            self.to_dec()
253                .checked_sub(rhs.to_dec())
254                .expect("Overflow in subtraction."),
255        )
256    }
257}
258
259impl Mul<&Ternary> for &Ternary {
260    type Output = Ternary;
261
262    fn mul(self, rhs: &Ternary) -> Self::Output {
263        Ternary::from_dec(
264            self.to_dec()
265                .checked_mul(rhs.to_dec())
266                .expect("Overflow in multiplication."),
267        )
268    }
269}
270
271impl Div<&Ternary> for &Ternary {
272    type Output = Ternary;
273
274    fn div(self, rhs: &Ternary) -> Self::Output {
275        Ternary::from_dec(
276            self.to_dec()
277                .checked_div(rhs.to_dec())
278                .expect("Overflow in division or division by zero."),
279        )
280    }
281}
282
283impl BitAnd<&Ternary> for &Ternary {
284    type Output = Ternary;
285
286    fn bitand(self, rhs: &Ternary) -> Self::Output {
287        if self.log() < rhs.log() {
288            return rhs & self;
289        }
290        let mut digits = Vec::new();
291        for (i, d) in self.digits.iter().rev().enumerate() {
292            let other = rhs.get_digit(i).unwrap_or(&Digit::Zero);
293            digits.push(*d & *other);
294        }
295        digits.reverse();
296        Ternary::new(digits)
297    }
298}
299
300impl BitOr<&Ternary> for &Ternary {
301    type Output = Ternary;
302
303    fn bitor(self, rhs: &Ternary) -> Self::Output {
304        if self.log() < rhs.log() {
305            return rhs | self;
306        }
307        let mut digits = Vec::new();
308        for (i, d) in self.digits.iter().rev().enumerate() {
309            let other = rhs.get_digit(i).unwrap_or(&Digit::Zero);
310            digits.push(*d | *other);
311        }
312        digits.reverse();
313        Ternary::new(digits)
314    }
315}
316
317impl BitXor<&Ternary> for &Ternary {
318    type Output = Ternary;
319
320    fn bitxor(self, rhs: &Ternary) -> Self::Output {
321        if self.log() < rhs.log() {
322            return rhs ^ self;
323        }
324        let mut digits = Vec::new();
325        for (i, d) in self.digits.iter().rev().enumerate() {
326            let other = rhs.get_digit(i).unwrap_or(&Digit::Zero);
327            digits.push(*d ^ *other);
328        }
329        digits.reverse();
330        Ternary::new(digits)
331    }
332}
333
334#[cfg(test)]
335#[test]
336fn test_ternary_ops() {
337    use alloc::string::ToString;
338
339    let repr9 = Ternary::parse("+00");
340    let repr4 = Ternary::parse("++");
341    let repr13 = &repr9 + &repr4;
342    let repr17 = &repr13 + &repr4;
343    let repr34 = &repr17 + &repr17;
344
345    assert_eq!(repr13.to_string(), "+++");
346    assert_eq!(repr17.to_string(), "+-0-");
347    assert_eq!(repr34.to_string(), "++-+");
348
349    let repr30 = &repr34 - &repr4;
350    assert_eq!(repr30.to_dec(), 30);
351    assert_eq!(repr30.to_string(), "+0+0");
352
353    let repr120 = &repr30 * &repr4;
354    assert_eq!(repr120.to_dec(), 120);
355    assert_eq!(repr120.to_string(), "++++0");
356
357    let repr_neg120 = -&repr120;
358    assert_eq!(repr_neg120.to_dec(), -120);
359    assert_eq!(repr_neg120.to_string(), "----0");
360
361    let bitwise = &Ternary::parse("++00") & &Ternary::parse("0000");
362    assert_eq!(bitwise.to_string(), "0000");
363
364    let bitwise = &Ternary::parse("++00") & &Ternary::parse("0+00");
365    assert_eq!(bitwise.to_string(), "0+00");
366
367    let bitwise = &Ternary::parse("+000") | &Ternary::parse("000-");
368    assert_eq!(bitwise.to_string(), "+000");
369
370
371    let bitwise = &Ternary::parse("+000") & &Ternary::parse("000-");
372    assert_eq!(bitwise.to_string(), "000-");
373
374    let bitwise = &Ternary::parse("+000") | &Ternary::parse("000+");
375    assert_eq!(bitwise.to_string(), "+00+");
376}