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