balanced_ternary/
tryte.rs

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