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