Skip to main content

vildrose_core/word/
basic.rs

1use super::Word;
2use crate::trit::Trit;
3use core::cmp::Ordering;
4use core::fmt;
5use core::ops::Neg;
6
7// TODO: Look into how to compact the words by storing 4 trits per byte
8
9impl<const N: usize> Word<N> {
10    /// Number of trits in this word.
11    pub const TRIT_COUNT: usize = N;
12
13    /// Creates a word from little-endian trits.
14    ///
15    /// The first array element is the least-significant trit.
16    #[must_use]
17    pub const fn new(trits: [Trit; N]) -> Self {
18        Self(trits)
19    }
20
21    /// Creates a word with all trits equal to zero.
22    #[must_use]
23    pub const fn zero() -> Self {
24        Self([Trit::Z; N])
25    }
26
27    /// Returns the stored trits in little-endian order.
28    #[must_use]
29    pub const fn into_trits(self) -> [Trit; N] {
30        self.0
31    }
32
33    /// Returns the trit at `index`, or `None` if it is out of bounds.
34    ///
35    /// Trit zero is the least-significant trit.
36    #[must_use]
37    pub fn get_trit(self, index: usize) -> Option<Trit> {
38        self.0.get(index).copied()
39    }
40
41    /// Returns the trit at `index`.
42    ///
43    /// # Panics
44    ///
45    /// Panics if `index >= Self::TRIT_COUNT`.
46    #[must_use]
47    pub const fn trit(self, index: usize) -> Trit {
48        self.0[index]
49    }
50
51    /// Returns the arithmetic negation of this word.
52    #[must_use]
53    pub fn negate(self) -> Self {
54        Self(self.0.map(Trit::negate))
55    }
56
57    /// Returns `Trit::N`, `Trit::Z`, or `Trit::P` according to numeric sign.
58    #[must_use]
59    pub fn sign(self) -> Trit {
60        self.0
61            .iter()
62            .rev()
63            .copied()
64            .find(|&trit| trit != Trit::Z)
65            .unwrap_or(Trit::Z)
66    }
67
68    /// Returns the non-negative magnitude of this word.
69    #[must_use]
70    pub fn abs(self) -> Self {
71        if self.sign() == Trit::N { -self } else { self }
72    }
73
74    /// Truncates this word to its least-significant `W` trits.
75    ///
76    /// # Panics
77    ///
78    /// Panics if `W > N`. This is a raw fixed-width truncation operation;
79    /// it does not check whether the value can be represented losslessly.
80    #[must_use]
81    pub fn truncate<const W: usize>(self) -> Word<W> {
82        assert!(W <= N, "cannot truncate Word<{N}> into wider Word<{W}>");
83
84        Word(core::array::from_fn(|index| self.0[index]))
85    }
86}
87
88impl<const N: usize> Neg for Word<N> {
89    type Output = Self;
90
91    fn neg(self) -> Self::Output {
92        self.negate()
93    }
94}
95
96impl<const N: usize> PartialOrd for Word<N> {
97    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
98        Some(self.cmp(other))
99    }
100}
101
102impl<const N: usize> Ord for Word<N> {
103    fn cmp(&self, other: &Self) -> Ordering {
104        self.0
105            .iter()
106            .rev()
107            .zip(other.0.iter().rev())
108            .map(|(left, right)| left.cmp(right))
109            .find(|&ordering| ordering != Ordering::Equal)
110            .unwrap_or(Ordering::Equal)
111    }
112}
113
114impl<const N: usize> fmt::Display for Word<N> {
115    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116        for trit in self.0.iter().rev() {
117            write!(f, "{trit}")?;
118        }
119
120        Ok(())
121    }
122}