Skip to main content

trit_vsa/
tryte.rs

1//! Tryte type representing 3 trits (values -13 to +13).
2//!
3//! A tryte is a balanced ternary byte, consisting of 3 trits.
4//! It can represent 27 distinct values (3^3 = 27), ranging from -13 to +13.
5
6use serde::{Deserialize, Serialize};
7use std::fmt;
8use std::ops::{Add, Mul, Neg};
9
10use crate::error::{Result, TernaryError};
11use crate::trit::Trit;
12
13/// Minimum value representable by a Tryte3 (-13).
14pub const TRYTE3_MIN: i32 = -13;
15/// Maximum value representable by a Tryte3 (+13).
16pub const TRYTE3_MAX: i32 = 13;
17
18/// A balanced ternary byte consisting of 3 trits.
19///
20/// # Value Range
21///
22/// A Tryte3 can represent values from -13 to +13:
23/// ```text
24/// Value = t0 * 1 + t1 * 3 + t2 * 9
25/// Min: -1 - 3 - 9 = -13
26/// Max: +1 + 3 + 9 = +13
27/// ```
28///
29/// # Internal Representation
30///
31/// Stored as a single u8 with the following encoding:
32/// - Bits 0-1: trit 0 (least significant)
33/// - Bits 2-3: trit 1
34/// - Bits 4-5: trit 2 (most significant)
35///
36/// Each trit is encoded as: 0=N(-1), 1=Z(0), 2=P(+1)
37///
38/// # Examples
39///
40/// ```
41/// use trit_vsa::Tryte3;
42///
43/// let t = Tryte3::from_value(5).unwrap();
44/// assert_eq!(t.value(), 5);
45///
46/// // Decompose into trits: 5 = -1 + (-1)*3 + 1*9 = -1 - 3 + 9
47/// let trits = t.to_trits();
48/// assert_eq!(trits[0].value(), -1);  // t0 = -1
49/// assert_eq!(trits[1].value(), -1);  // t1 = -1
50/// assert_eq!(trits[2].value(), 1);   // t2 = +1
51/// ```
52#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize)]
53pub struct Tryte3(u8);
54
55impl Tryte3 {
56    /// Create a tryte from an integer value.
57    ///
58    /// # Arguments
59    ///
60    /// * `value` - Integer value (-13 to +13)
61    ///
62    /// # Errors
63    ///
64    /// Returns `TernaryError::InvalidTryteValue` if value is outside range.
65    ///
66    /// # Examples
67    ///
68    /// ```
69    /// use trit_vsa::Tryte3;
70    ///
71    /// let t = Tryte3::from_value(7).unwrap();
72    /// assert_eq!(t.value(), 7);
73    ///
74    /// assert!(Tryte3::from_value(14).is_err());
75    /// assert!(Tryte3::from_value(-14).is_err());
76    /// ```
77    pub fn from_value(value: i32) -> Result<Self> {
78        if !(TRYTE3_MIN..=TRYTE3_MAX).contains(&value) {
79            return Err(TernaryError::InvalidTryteValue(value));
80        }
81
82        let trits = Self::value_to_trits(value);
83        Ok(Self::from_trits(trits))
84    }
85
86    /// Create a tryte from three trits.
87    ///
88    /// # Arguments
89    ///
90    /// * `trits` - Array of 3 trits [t0, t1, t2] where t0 is least significant
91    ///
92    /// # Examples
93    ///
94    /// ```
95    /// use trit_vsa::{Tryte3, Trit};
96    ///
97    /// let t = Tryte3::from_trits([Trit::P, Trit::Z, Trit::N]);
98    /// // Value = 1 + 0*3 + (-1)*9 = 1 - 9 = -8
99    /// assert_eq!(t.value(), -8);
100    /// ```
101    #[must_use]
102    pub fn from_trits(trits: [Trit; 3]) -> Self {
103        let encoded = Self::encode_trit(trits[0])
104            | (Self::encode_trit(trits[1]) << 2)
105            | (Self::encode_trit(trits[2]) << 4);
106        Self(encoded)
107    }
108
109    /// Get the integer value of the tryte.
110    #[must_use]
111    pub fn value(self) -> i32 {
112        let trits = self.to_trits();
113        trits[0].value() as i32 + trits[1].value() as i32 * 3 + trits[2].value() as i32 * 9
114    }
115
116    /// Extract the three trits.
117    ///
118    /// # Returns
119    ///
120    /// Array [t0, t1, t2] where t0 is least significant.
121    #[must_use]
122    pub fn to_trits(self) -> [Trit; 3] {
123        [
124            Self::decode_trit(self.0 & 0b11),
125            Self::decode_trit((self.0 >> 2) & 0b11),
126            Self::decode_trit((self.0 >> 4) & 0b11),
127        ]
128    }
129
130    /// Get a specific trit by index.
131    ///
132    /// # Arguments
133    ///
134    /// * `index` - Trit index (0, 1, or 2)
135    ///
136    /// # Panics
137    ///
138    /// Panics if index >= 3.
139    #[must_use]
140    pub fn get_trit(self, index: usize) -> Trit {
141        assert!(index < 3, "trit index out of bounds");
142        Self::decode_trit((self.0 >> (index * 2)) & 0b11)
143    }
144
145    /// Create a zero tryte.
146    #[must_use]
147    pub const fn zero() -> Self {
148        // All trits are Z (encoded as 1), so: 01|01|01 = 0b010101 = 21
149        Self(0b01_01_01)
150    }
151
152    /// Check if the tryte is zero.
153    #[must_use]
154    pub fn is_zero(self) -> bool {
155        self.value() == 0
156    }
157
158    /// Get the raw packed representation.
159    #[must_use]
160    pub const fn raw(self) -> u8 {
161        self.0
162    }
163
164    // Internal: convert value to trit array using balanced ternary conversion
165    fn value_to_trits(mut value: i32) -> [Trit; 3] {
166        let mut trits = [Trit::Z; 3];
167
168        for trit in &mut trits {
169            if value == 0 {
170                *trit = Trit::Z;
171                continue;
172            }
173
174            let mut rem = value % 3;
175            value /= 3;
176
177            // Adjust for balanced ternary
178            if rem == 2 {
179                rem = -1;
180                value += 1;
181            } else if rem == -2 {
182                rem = 1;
183                value -= 1;
184            }
185
186            *trit = match rem {
187                -1 => Trit::N,
188                0 => Trit::Z,
189                1 => Trit::P,
190                _ => unreachable!(),
191            };
192        }
193
194        trits
195    }
196
197    // Internal: encode a trit to 2 bits (0=N, 1=Z, 2=P)
198    fn encode_trit(trit: Trit) -> u8 {
199        match trit {
200            Trit::N => 0,
201            Trit::Z => 1,
202            Trit::P => 2,
203        }
204    }
205
206    // Internal: decode 2 bits to a trit
207    fn decode_trit(bits: u8) -> Trit {
208        match bits & 0b11 {
209            0 => Trit::N,
210            1 | 3 => Trit::Z, // 3 is invalid, treat as zero
211            2 => Trit::P,
212            _ => unreachable!(),
213        }
214    }
215}
216
217impl Default for Tryte3 {
218    fn default() -> Self {
219        Self::zero()
220    }
221}
222
223impl Neg for Tryte3 {
224    type Output = Self;
225
226    fn neg(self) -> Self::Output {
227        let trits = self.to_trits();
228        Self::from_trits([-trits[0], -trits[1], -trits[2]])
229    }
230}
231
232impl Add for Tryte3 {
233    type Output = (Self, Trit);
234
235    /// Add two trytes, returning (result, carry).
236    fn add(self, other: Self) -> Self::Output {
237        let a = self.to_trits();
238        let b = other.to_trits();
239        let mut result = [Trit::Z; 3];
240        let mut carry = Trit::Z;
241
242        for i in 0..3 {
243            // Add trits and carry
244            let (sum1, carry1) = a[i].add_with_carry(b[i]);
245            let (sum2, carry2) = sum1.add_with_carry(carry);
246
247            result[i] = sum2;
248            // Combine carries
249            let (carry_sum, _) = carry1.add_with_carry(carry2);
250            carry = carry_sum;
251        }
252
253        (Self::from_trits(result), carry)
254    }
255}
256
257impl Mul for Tryte3 {
258    type Output = (Self, Self);
259
260    /// Multiply two trytes, returning (low, high) result.
261    ///
262    /// The full result is `low + high * 27`.
263    fn mul(self, other: Self) -> Self::Output {
264        let product = self.value() * other.value();
265
266        // Balanced ternary division for 6-trit result
267        let low_val = ((product % 27) + 27 + 13) % 27 - 13;
268        let high_val = (product - low_val) / 27;
269
270        (
271            Self::from_value(low_val).unwrap_or_else(|_| Self::zero()),
272            Self::from_value(high_val.clamp(TRYTE3_MIN, TRYTE3_MAX))
273                .unwrap_or_else(|_| Self::zero()),
274        )
275    }
276}
277
278impl fmt::Debug for Tryte3 {
279    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
280        let trits = self.to_trits();
281        write!(
282            f,
283            "Tryte3({}{}{} = {})",
284            trits[2],
285            trits[1],
286            trits[0],
287            self.value()
288        )
289    }
290}
291
292impl fmt::Display for Tryte3 {
293    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
294        write!(f, "{}", self.value())
295    }
296}
297
298impl TryFrom<i32> for Tryte3 {
299    type Error = TernaryError;
300
301    fn try_from(value: i32) -> Result<Self> {
302        Self::from_value(value)
303    }
304}
305
306impl From<Tryte3> for i32 {
307    fn from(tryte: Tryte3) -> Self {
308        tryte.value()
309    }
310}
311
312#[cfg(test)]
313mod tests {
314    use super::*;
315
316    #[test]
317    fn test_tryte_range() {
318        // Test all valid values
319        for v in TRYTE3_MIN..=TRYTE3_MAX {
320            let t = Tryte3::from_value(v).expect("valid value");
321            assert_eq!(t.value(), v);
322        }
323
324        // Test invalid values
325        assert!(Tryte3::from_value(TRYTE3_MIN - 1).is_err());
326        assert!(Tryte3::from_value(TRYTE3_MAX + 1).is_err());
327    }
328
329    #[test]
330    fn test_tryte_zero() {
331        let z = Tryte3::zero();
332        assert_eq!(z.value(), 0);
333        assert!(z.is_zero());
334    }
335
336    #[test]
337    fn test_tryte_trits_roundtrip() {
338        for v in TRYTE3_MIN..=TRYTE3_MAX {
339            let t = Tryte3::from_value(v).unwrap();
340            let trits = t.to_trits();
341            let reconstructed = Tryte3::from_trits(trits);
342            assert_eq!(reconstructed.value(), v);
343        }
344    }
345
346    #[test]
347    fn test_tryte_negation() {
348        for v in TRYTE3_MIN..=TRYTE3_MAX {
349            let t = Tryte3::from_value(v).unwrap();
350            let neg = -t;
351            assert_eq!(neg.value(), -v);
352        }
353    }
354
355    #[test]
356    fn test_tryte_addition() {
357        // Test some specific additions
358        let a = Tryte3::from_value(5).unwrap();
359        let b = Tryte3::from_value(3).unwrap();
360        let (result, carry) = a + b;
361        assert_eq!(result.value() + carry.value() as i32 * 27, 8);
362
363        // Test with overflow
364        let a = Tryte3::from_value(13).unwrap();
365        let b = Tryte3::from_value(1).unwrap();
366        let (result, carry) = a + b;
367        // 13 + 1 = 14, which needs carry
368        let total = result.value() + carry.value() as i32 * 27;
369        assert_eq!(total, 14);
370    }
371
372    #[test]
373    fn test_tryte_multiplication() {
374        // Small values
375        let a = Tryte3::from_value(3).unwrap();
376        let b = Tryte3::from_value(4).unwrap();
377        let (low, high) = a * b;
378        let total = low.value() + high.value() * 27;
379        assert_eq!(total, 12);
380
381        // Test with larger values
382        let a = Tryte3::from_value(10).unwrap();
383        let b = Tryte3::from_value(10).unwrap();
384        let (low, high) = a * b;
385        let total = low.value() + high.value() * 27;
386        assert_eq!(total, 100);
387    }
388
389    #[test]
390    fn test_tryte_get_trit() {
391        let t = Tryte3::from_trits([Trit::N, Trit::Z, Trit::P]);
392        assert_eq!(t.get_trit(0), Trit::N);
393        assert_eq!(t.get_trit(1), Trit::Z);
394        assert_eq!(t.get_trit(2), Trit::P);
395    }
396
397    #[test]
398    fn test_tryte_specific_values() {
399        // Test value 5: should be -1 + 0*3 + 1*9 = -1 + 9 = 8? No...
400        // Actually: 5 = 5*1 in balanced ternary:
401        // 5 / 3 = 1 remainder 2 -> trit = -1, new value = 2
402        // 2 / 3 = 0 remainder 2 -> trit = -1, new value = 1
403        // 1 / 3 = 0 remainder 1 -> trit = +1, done
404        // So 5 = [-1, -1, 1] = -1 + (-1)*3 + 1*9 = -1 - 3 + 9 = 5 ✓
405
406        let t = Tryte3::from_value(5).unwrap();
407        let trits = t.to_trits();
408        let reconstructed =
409            trits[0].value() as i32 + trits[1].value() as i32 * 3 + trits[2].value() as i32 * 9;
410        assert_eq!(reconstructed, 5);
411    }
412}