balanced_ternary/
tryte.rs

1//! A module defining the `BalancedTryte` structure and its associated functionality.
2//!
3//! The `BalancedTryte` struct represents a Copy type balanced ternary number with exactly 6 digits.
4//! Each digit in a balanced ternary system can have one of three values: -1, 0, or 1.
5//!
6//! This module provides utilities to convert between `BalancedTryte` and various
7//! representations such as `Ternary`, `u8`, and `u16`. It ensures that the `BalancedTryte`
8//! always consists of exactly 6 ternary digits.
9//!
10//! A [Tryte] can holds value between `-364` and `+364`.
11
12use crate::{Digit, Ternary};
13use alloc::string::ToString;
14use core::fmt::{Display, Formatter};
15use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg, Not, Sub};
16
17/// A struct representing a balanced ternary number with a fixed length of 6 digits.
18///
19/// The underlying representation of the number is an array of six `Digit` values.
20/// This struct provides conversion methods to and from other formats.
21#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)]
22pub struct Tryte {
23    /// The raw representation of the `BalancedTryte` as 6 ternary digits.
24    raw: [Digit; 6],
25}
26
27impl Tryte {
28    /// `364` or `++++++`
29    pub const MAX: Self = Self {
30        raw: [Digit::Pos; 6],
31    };
32    /// `-364` or `------`
33    pub const MIN: Self = Self {
34        raw: [Digit::Neg; 6],
35    };
36    /// `0` or `000000`
37    pub const ZERO: Self = Self {
38        raw: [Digit::Zero; 6],
39    };
40
41    /// Converts the `BalancedTryte` into its `Ternary` representation.
42    ///
43    /// # Returns
44    ///
45    /// A `Ternary` object representing the same balanced ternary number.
46    pub fn to_ternary(&self) -> Ternary {
47        Ternary::new(self.raw.to_vec())
48    }
49
50    /// Retrieves a slice containing the digits of the `BalancedTryte`.
51    ///
52    /// # Returns
53    ///
54    /// A slice referencing the six-digit array of the `BalancedTryte`.
55    ///
56    /// This function allows access to the raw representation of the
57    /// balanced ternary number as a slice of `Digit` values.
58    pub fn to_digit_slice(&self) -> &[Digit] {
59        &self.raw
60    }
61
62    /// Creates a `BalancedTryte` from the given `Ternary`.
63    ///
64    /// # Arguments
65    ///
66    /// * `v` - A reference to a `Ternary` object.
67    ///
68    /// # Panics
69    ///
70    /// This function panics if the `Ternary` contains more than 6 digits.
71    pub fn from_ternary(v: &Ternary) -> Self {
72        if v.log() > 6 {
73            panic!("Cannot convert a Ternary with more than 6 digits to a Tryte.");
74        }
75        let mut digits = [Digit::Zero; 6];
76        for (i, d) in v.digits.iter().rev().enumerate() {
77            digits[5 - i] = *d;
78        }
79        Self { raw: digits }
80    }
81
82    /// Converts the `BalancedTryte` into a signed 16-bit integer.
83    ///
84    /// # Returns
85    ///
86    /// A `i16` representing the decimal value of the `BalancedTryte`.
87    pub fn to_i16(&self) -> i16 {
88        self.to_ternary().to_dec() as i16
89    }
90
91    /// Creates a `BalancedTryte` from a signed 8-bit integer.
92    ///
93    /// # Arguments
94    ///
95    /// * `v` - An unsigned 8-bit integer.
96    ///
97    /// # Returns
98    ///
99    /// A `BalancedTryte` representing the equivalent ternary number.
100    pub fn from_i8(v: i8) -> Self {
101        Self::from_ternary(&Ternary::from_dec(v as i64))
102    }
103
104    /// Creates a `BalancedTryte` from a signed 16-bit integer.
105    ///
106    /// # Arguments
107    ///
108    /// * `v` - A signed 16-bit integer.
109    ///
110    /// # Returns
111    ///
112    /// A `BalancedTryte` representing the equivalent ternary number.
113    pub fn from_i16(v: i16) -> Self {
114        Self::from_ternary(&Ternary::from_dec(v as i64))
115    }
116
117    /// Retrieves the digit at the specified index in the `BalancedTryte`.
118    ///
119    /// # Arguments
120    ///
121    /// * `index` - The index of the digit to retrieve (0-based, right-to-left).
122    ///
123    /// # Returns
124    ///
125    /// The `Digit` at the specified index.
126    ///
127    /// # Panics
128    ///
129    /// This function panics if the index is greater than 5.
130    pub fn digit(&self, index: usize) -> Digit {
131        if index > 5 {
132            panic!(
133                "Cannot access a digit at index {}. Tryte has only 6 digits.",
134                index
135            );
136        }
137        *self.raw.iter().rev().nth(index).unwrap()
138    }
139}
140
141impl Display for Tryte {
142    /// Formats the `BalancedTryte` for display.
143    ///
144    /// The `BalancedTryte` is displayed in its balanced ternary representation
145    /// as a 6-character string.
146    fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
147        write!(f, "{:06}", self.to_ternary().to_string())
148    }
149}
150
151impl Neg for Tryte {
152    type Output = Tryte;
153    fn neg(self) -> Self::Output {
154        Self::from_ternary(&-&self.to_ternary())
155    }
156}
157
158impl Add for Tryte {
159    type Output = Tryte;
160
161    fn add(self, rhs: Self) -> Self::Output {
162        Self::from_ternary(&(&self.to_ternary() + &rhs.to_ternary()))
163    }
164}
165
166impl Sub for Tryte {
167    type Output = Tryte;
168
169    fn sub(self, rhs: Self) -> Self::Output {
170        Self::from_ternary(&(&self.to_ternary() - &rhs.to_ternary()))
171    }
172}
173
174impl Mul for Tryte {
175    type Output = Tryte;
176
177    fn mul(self, rhs: Self) -> Self::Output {
178        Self::from_ternary(&(&self.to_ternary() * &rhs.to_ternary()))
179    }
180}
181
182impl Div for Tryte {
183    type Output = Tryte;
184
185    fn div(self, rhs: Self) -> Self::Output {
186        Self::from_ternary(&(&self.to_ternary() / &rhs.to_ternary()))
187    }
188}
189
190impl BitAnd for Tryte {
191    type Output = Tryte;
192    fn bitand(self, rhs: Self) -> Self::Output {
193        Self::from_ternary(&(&self.to_ternary() & &rhs.to_ternary()))
194    }
195}
196
197impl BitOr for Tryte {
198    type Output = Tryte;
199    fn bitor(self, rhs: Self) -> Self::Output {
200        Self::from_ternary(&(&self.to_ternary() | &rhs.to_ternary()))
201    }
202}
203
204impl BitXor for Tryte {
205    type Output = Tryte;
206    fn bitxor(self, rhs: Self) -> Self::Output {
207        Self::from_ternary(&(&self.to_ternary() ^ &rhs.to_ternary()))
208    }
209}
210
211impl Not for Tryte {
212    type Output = Tryte;
213    fn not(self) -> Self::Output {
214        -self
215    }
216}
217
218impl From<Ternary> for Tryte {
219    fn from(value: Ternary) -> Self {
220        Tryte::from_ternary(&value)
221    }
222}
223
224impl From<Tryte> for Ternary {
225    fn from(value: Tryte) -> Self {
226        value.to_ternary()
227    }
228}
229
230#[cfg(test)]
231#[test]
232pub fn test_tryte() {
233    let tryte = Tryte::from_i16(255);
234    assert_eq!(tryte.to_i16(), 255);
235    assert_eq!(tryte.to_string(), "+00++0");
236
237    let tryte = Tryte::from_i8(16);
238    assert_eq!(tryte.to_i16(), 16);
239    assert_eq!(tryte.to_string(), "00+--+");
240
241    assert_eq!(Tryte::MAX.to_string(), "++++++");
242    assert_eq!(Tryte::MAX.to_i16(), 364);
243    assert_eq!(Tryte::MIN.to_string(), "------");
244    assert_eq!(Tryte::MIN.to_i16(), -364);
245    assert_eq!(Tryte::ZERO.to_string(), "000000");
246    assert_eq!(Tryte::ZERO.to_i16(), 0);
247}