balanced_ternary/
digit.rs

1//! ## Module: Balanced Ternary `Digit`
2//!
3//! This module defines the `Digit` type for the balanced ternary numeral system,
4//! along with its associated operations and functionality.
5//!
6//! ### Key Features
7//!
8//! - **`Digit` Type**: Represents a digit in the balanced ternary numeral system.
9//!     - Possible values: `Neg` (-1), `Zero` (0), `Pos` (+1).
10//!     - Provides utility functions for converting between characters, integers, and other formats.
11//! - **Arithmetic Operators**: Implements arithmetic operations for digits, including:
12//!     - Negation (`Neg`) and Bitwise Not (`Not`).
13//!     - Addition (`Add`) and Subtraction (`Sub`).
14//!     - Multiplication (`Mul`) and Division (`Div`), with safe handling of divisors (division by zero panics).
15//! - **Logical Operators**: Supports bitwise logical operations (AND, OR, XOR) based on ternary logic rules.
16//! - **Custom Methods**: Additional utility methods implementing balanced ternary logic principles.
17//!
18//! ### Supported Use Cases
19//!
20//! - Arithmetic in balanced ternary numeral systems.
21//! - Logic operations in custom numeral systems.
22//! - Conversion between balanced ternary representation and more common formats like integers and characters.
23//!
24//! ## `Digit` type arithmetical and logical operations
25//!
26//! - `Neg` and `Not` for `Digit`: Negates the digit value, adhering to balanced ternary rules.
27//! - `Add<Digit>` for `Digit`: Adds two `Digit` values and returns a `Ternary`.
28//! - `Sub<Digit>` for `Digit`: Subtracts one `Digit` from another and returns a `Ternary`.
29//! - `Mul<Digit>` for `Digit`: Multiplies two `Digit` values and returns a `Digit`.
30//! - `Div<Digit>` for `Digit`: Divides one `Digit` by another and returns a `Digit`. Division by zero panics.
31//!
32//! ### Logical Operations for `Digit`
33//!
34//! The `Digit` type supports bitwise logical operations, which are implemented according to logical rules applicable to balanced ternary digits.
35//!
36//! #### `BitAnd` for `Digit`
37//!
38//! Performs a bitwise AND operation between two `Digit` values.
39//!
40//! - `Digit::Neg & other` → `Digit::Neg`
41//! - `Digit::Zero & Digit::Neg` → `Digit::Neg`
42//! - `Digit::Zero & other` → `Digit::Zero`
43//! - `Digit::Pos & other` → `other`
44//!
45//! #### `BitOr` for `Digit`
46//!
47//! Performs a bitwise OR operation between two `Digit` values.
48//!
49//! - `Digit::Neg | other` → `other`
50//! - `Digit::Zero | Digit::Pos` → `Digit::Pos`
51//! - `Digit::Zero | other` → `Digit::Zero`
52//! - `Digit::Pos | other` → `Digit::Pos`
53//!
54//! #### `BitXor` for `Digit`
55//!
56//! Performs a bitwise XOR operation between two `Digit` values.
57//!
58//! - `Digit::Neg ^ other` → `other`
59//! - `Digit::Zero ^ other` → `Digit::Zero`
60//! - `Digit::Pos ^ other` → `-other`
61
62use crate::Ternary;
63use alloc::vec;
64use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Sub};
65
66/// Represents a digit in the balanced ternary numeral system.
67///
68/// A digit can have one of three values:
69/// - `Neg` (-1): Represents the value -1 in the balanced ternary system.
70/// - `Zero` (0): Represents the value 0 in the balanced ternary system.
71/// - `Pos` (+1): Represents the value +1 in the balanced ternary system.
72///
73/// Provides utility functions for converting to/from characters, integers, and negation.
74#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)]
75pub enum Digit {
76    /// Represents -1
77    Neg,
78    /// Represents 0
79    Zero,
80    /// Represents +1
81    Pos,
82}
83
84impl Digit {
85    /// Converts the `Digit` into its character representation.
86    ///
87    /// - Returns:
88    ///     - `-` for `Digit::Neg`
89    ///     - `0` for `Digit::Zero`
90    ///     - `+` for `Digit::Pos`
91    pub fn to_char(&self) -> char {
92        match self {
93            Digit::Neg => '-',
94            Digit::Zero => '0',
95            Digit::Pos => '+',
96        }
97    }
98
99    /// Creates a `Digit` from its character representation.
100    ///
101    /// - Accepts:
102    ///     - `-` for `Digit::Neg`
103    ///     - `0` for `Digit::Zero`
104    ///     - `+` for `Digit::Pos`
105    /// - Panics if the input character is invalid.
106    pub fn from_char(c: char) -> Digit {
107        match c {
108            '-' => Digit::Neg,
109            '0' => Digit::Zero,
110            '+' => Digit::Pos,
111            _ => panic!("Invalid value. A Ternary must be either -, 0 or +."),
112        }
113    }
114
115    /// Converts the `Digit` into its integer representation.
116    ///
117    /// - Returns:
118    ///     - -1 for `Digit::Neg`
119    ///     - 0 for `Digit::Zero`
120    ///     - 1 for `Digit::Pos`
121    pub fn to_i8(&self) -> i8 {
122        match self {
123            Digit::Neg => -1,
124            Digit::Zero => 0,
125            Digit::Pos => 1,
126        }
127    }
128
129    /// Creates a `Digit` from its integer representation.
130    ///
131    /// - Accepts:
132    ///     - -1 for `Digit::Neg`
133    ///     - 0 for `Digit::Zero`
134    ///     - 1 for `Digit::Pos`
135    /// - Panics if the input integer is invalid.
136    pub fn from_i8(i: i8) -> Digit {
137        match i {
138            -1 => Digit::Neg,
139            0 => Digit::Zero,
140            1 => Digit::Pos,
141            _ => panic!("Invalid value. A Ternary must be either -1, 0 or +1."),
142        }
143    }
144    /// Returns the corresponding possible value of the current `Digit`.
145    ///
146    /// - Returns:
147    ///     - `Digit::Neg` for `Digit::Neg`
148    ///     - `Digit::Pos` for `Digit::Zero`
149    ///     - `Digit::Pos` for `Digit::Pos`
150    pub fn possibly(&self) -> Self {
151        match self {
152            Digit::Neg => Digit::Neg,
153            Digit::Zero => Digit::Pos,
154            Digit::Pos => Digit::Pos,
155        }
156    }
157
158    /// Determines the condition of necessity for the current `Digit`.
159    ///
160    /// - Returns:
161    ///     - `Digit::Neg` for `Digit::Neg`
162    ///     - `Digit::Neg` for `Digit::Zero`
163    ///     - `Digit::Pos` for `Digit::Pos`
164    ///
165    /// This method is used to calculate necessity as part
166    /// of balanced ternary logic systems.
167    pub fn necessary(&self) -> Self {
168        match self {
169            Digit::Neg => Digit::Neg,
170            Digit::Zero => Digit::Neg,
171            Digit::Pos => Digit::Pos,
172        }
173    }
174
175    /// Determines the condition of contingency for the current `Digit`.
176    ///
177    /// - Returns:
178    ///     - `Digit::Neg` for `Digit::Neg`
179    ///     - `Digit::Pos` for `Digit::Zero`
180    ///     - `Digit::Neg` for `Digit::Pos`
181    ///
182    /// This method represents contingency in balanced ternary logic,
183    /// which defines the specific alternation of `Digit` values.
184    pub fn contingently(&self) -> Self {
185        match self {
186            Digit::Neg => Digit::Neg,
187            Digit::Zero => Digit::Pos,
188            Digit::Pos => Digit::Neg,
189        }
190    }
191
192    /// Determines the condition of non-negativity for the current `Digit`.
193    ///
194    /// - Returns:
195    ///     - `Digit::Zero` for `Digit::Neg`
196    ///     - `Digit::Pos` for `Digit::Zero`
197    ///     - `Digit::Pos` for `Digit::Pos`
198    ///
199    /// This method is used to filter out negative conditions
200    /// in computations with balanced ternary representations.
201    pub fn not_negative(&self) -> Self {
202        match self {
203            Digit::Neg => Digit::Zero,
204            Digit::Zero => Digit::Pos,
205            Digit::Pos => Digit::Pos,
206        }
207    }
208
209    /// Determines the strictly positive condition for the current `Digit`.
210    ///
211    /// - Returns:
212    ///     - `Digit::Zero` for `Digit::Neg`
213    ///     - `Digit::Zero` for `Digit::Zero`
214    ///     - `Digit::Pos` for `Digit::Pos`
215    ///
216    /// This method is used to calculate strictly positive states
217    /// in association with ternary logic.
218    pub fn positive(&self) -> Self {
219        match self {
220            Digit::Neg => Digit::Zero,
221            Digit::Zero => Digit::Zero,
222            Digit::Pos => Digit::Pos,
223        }
224    }
225
226    /// Determines the condition of non-positivity for the current `Digit`.
227    ///
228    /// - Returns:
229    ///     - `Digit::Pos` for `Digit::Neg`
230    ///     - `Digit::Pos` for `Digit::Zero`
231    ///     - `Digit::Zero` for `Digit::Pos`
232    ///
233    /// This method complements the `positive` condition and captures
234    /// states that are not strictly positive.
235    pub fn not_positive(&self) -> Self {
236        match self {
237            Digit::Neg => Digit::Pos,
238            Digit::Zero => Digit::Pos,
239            Digit::Pos => Digit::Zero,
240        }
241    }
242
243    /// Determines the strictly negative condition for the current `Digit`.
244    ///
245    /// - Returns:
246    ///     - `Digit::Pos` for `Digit::Neg`
247    ///     - `Digit::Zero` for `Digit::Zero`
248    ///     - `Digit::Zero` for `Digit::Pos`
249    ///
250    /// This method calculates strictly negative states
251    /// in association with ternary logic.
252    pub fn negative(&self) -> Self {
253        match self {
254            Digit::Neg => Digit::Pos,
255            Digit::Zero => Digit::Zero,
256            Digit::Pos => Digit::Zero,
257        }
258    }
259
260    /// Performs Kleene implication with the current `Digit` as `self` and another `Digit`.
261    ///
262    /// - `self`: The antecedent of the implication.
263    /// - `other`: The consequent of the implication.
264    ///
265    /// - Returns:
266    ///     - `Digit::Pos` when `self` is `Digit::Neg`.
267    ///     - The positive condition of `other` when `self` is `Digit::Zero`.
268    ///     - `other` when `self` is `Digit::Pos`.
269    ///
270    /// Implements Kleene ternary implication logic, which includes
271    /// determining the logical result based on antecedent and consequent.
272    pub fn k3_imply(&self, other: Self) -> Self {
273        match self {
274            Digit::Neg => Digit::Pos,
275            Digit::Zero => other.positive(),
276            Digit::Pos => other,
277        }
278    }
279
280    /// Performs Łukasiewicz implication with the current `Digit` as `self` and another `Digit`.
281    ///
282    /// - `self`: The antecedent of the implication.
283    /// - `other`: The consequent of the implication.
284    ///
285    /// - Returns:
286    ///     - `Digit::Pos` when `self` is `Digit::Neg`.
287    ///     - The non-negative condition of `other` when `self` is `Digit::Zero`.
288    ///     - `other` when `self` is `Digit::Pos`.
289    ///
290    /// Implements Łukasiewicz ternary implication logic, which
291    /// evaluates an alternative approach for implication compared to Kleene logic.
292    pub fn l3_imply(&self, other: Self) -> Self {
293        match self {
294            Digit::Neg => Digit::Pos,
295            Digit::Zero => other.not_negative(),
296            Digit::Pos => other,
297        }
298    }
299
300    /// Performs RM3 implication with the current `Digit` as `self` and another `Digit`.
301    ///
302    /// - `self`: The antecedent of the implication.
303    /// - `other`: The consequent of the implication.
304    ///
305    /// - Returns:
306    ///     - `Digit::Pos` when `self` is `Digit::Neg`.
307    ///     - `other` when `self` is `Digit::Zero`.
308    ///     - The necessary condition of `other` when `self` is `Digit::Pos`.
309    ///
310    /// Implements RM3 ternary implication logic, which defines a unique
311    /// perspective for implication operations in balanced ternary systems.
312    pub fn rm3_imply(&self, other: Self) -> Self {
313        match self {
314            Digit::Neg => Digit::Pos,
315            Digit::Zero => other,
316            Digit::Pos => other.necessary(),
317        }
318    }
319
320    /// Performs HT implication with the current `Digit` as `self` and another `Digit`.
321    ///
322    /// - `self`: The antecedent of the implication.
323    /// - `other`: The consequent of the implication.
324    ///
325    /// - Returns:
326    ///     - `Digit::Pos` when `self` is `Digit::Neg`.
327    ///     - The possibility condition of `other` when `self` is `Digit::Zero`.
328    ///     - `other` when `self` is `Digit::Pos`.
329    ///
330    /// This method computes HT ternary implication based on heuristic logic.
331    pub fn ht_imply(&self, other: Self) -> Self {
332        match self {
333            Digit::Neg => Digit::Pos,
334            Digit::Zero => other.possibly(),
335            Digit::Pos => other,
336        }
337    }
338
339    /// Performs HT logical negation of the current `Digit`.
340    ///
341    /// - Returns:
342    ///     - `Digit::Pos` when `self` is `Digit::Neg`.
343    ///     - `Digit::Neg` when `self` is `Digit::Zero` or `Digit::Pos`.
344    ///
345    /// This method evaluates the HT negation result using heuristic ternary logic.
346    pub fn ht_not(&self) -> Self {
347        match self {
348            Digit::Neg => Digit::Pos,
349            Digit::Zero => Digit::Neg,
350            Digit::Pos => Digit::Neg,
351        }
352    }
353}
354
355impl Neg for Digit {
356    type Output = Self;
357
358    /// Returns the negation of the `Digit`.
359    ///
360    /// - `Digit::Neg` becomes `Digit::Pos`
361    /// - `Digit::Pos` becomes `Digit::Neg`
362    /// - `Digit::Zero` remains `Digit::Zero`
363    fn neg(self) -> Self::Output {
364        match self {
365            Digit::Neg => Digit::Pos,
366            Digit::Zero => Digit::Zero,
367            Digit::Pos => Digit::Neg,
368        }
369    }
370}
371
372impl Not for Digit {
373    type Output = Self;
374    fn not(self) -> Self::Output {
375        -self
376    }
377}
378
379impl Add<Digit> for Digit {
380    type Output = Ternary;
381    fn add(self, other: Digit) -> Self::Output {
382        match self {
383            Digit::Neg => match other {
384                Digit::Neg => Ternary::parse("-+"),
385                Digit::Zero => Ternary::parse("-"),
386                Digit::Pos => Ternary::parse("0"),
387            },
388            Digit::Zero => Ternary::new(vec![other]),
389            Digit::Pos => match other {
390                Digit::Neg => Ternary::parse("0"),
391                Digit::Zero => Ternary::parse("+"),
392                Digit::Pos => Ternary::parse("+-"),
393            },
394        }
395    }
396}
397
398impl Sub<Digit> for Digit {
399    type Output = Ternary;
400    fn sub(self, other: Digit) -> Self::Output {
401        match self {
402            Digit::Neg => match other {
403                Digit::Neg => Ternary::parse("0"),
404                Digit::Zero => Ternary::parse("-"),
405                Digit::Pos => Ternary::parse("-+"),
406            },
407            Digit::Zero => Ternary::new(vec![-other]),
408            Digit::Pos => match other {
409                Digit::Neg => Ternary::parse("+-"),
410                Digit::Zero => Ternary::parse("+"),
411                Digit::Pos => Ternary::parse("0"),
412            },
413        }
414    }
415}
416
417impl Mul<Digit> for Digit {
418    type Output = Digit;
419
420    fn mul(self, other: Digit) -> Self::Output {
421        match self {
422            Digit::Neg => -other,
423            Digit::Zero => Digit::Zero,
424            Digit::Pos => other,
425        }
426    }
427}
428
429impl Div<Digit> for Digit {
430    type Output = Digit;
431
432    fn div(self, other: Digit) -> Self::Output {
433        match self {
434            Digit::Neg => match other {
435                Digit::Neg => Digit::Pos,
436                Digit::Zero => panic!("Cannot divide by zero."),
437                Digit::Pos => Digit::Neg,
438            },
439            Digit::Zero => match other {
440                Digit::Neg => Digit::Zero,
441                Digit::Zero => panic!("Cannot divide by zero."),
442                Digit::Pos => Digit::Zero,
443            },
444            Digit::Pos => match other {
445                Digit::Neg => Digit::Neg,
446                Digit::Zero => panic!("Cannot divide by zero."),
447                Digit::Pos => Digit::Pos,
448            },
449        }
450    }
451}
452
453impl BitAnd for Digit {
454    type Output = Self;
455    fn bitand(self, other: Self) -> Self::Output {
456        match self {
457            Digit::Neg => Digit::Neg,
458            Digit::Zero => match other {
459                Digit::Neg => Digit::Neg,
460                _ => Digit::Zero,
461            },
462            Digit::Pos => other,
463        }
464    }
465}
466
467impl BitOr for Digit {
468    type Output = Self;
469    fn bitor(self, other: Self) -> Self::Output {
470        match self {
471            Digit::Neg => other,
472            Digit::Zero => match other {
473                Digit::Pos => Digit::Pos,
474                _ => Digit::Zero,
475            },
476            Digit::Pos => Digit::Pos,
477        }
478    }
479}
480
481impl BitXor for Digit {
482    type Output = Self;
483
484    fn bitxor(self, rhs: Self) -> Self::Output {
485        match self {
486            Digit::Neg => rhs,
487            Digit::Zero => Digit::Zero,
488            Digit::Pos => -rhs,
489        }
490    }
491}