balanced_ternary/
tryte.rs1use crate::{
2 Digit,
3 Digit::{Neg, Pos, Zero},
4 Ternary,
5};
6use alloc::string::{String, ToString};
7use alloc::vec::Vec;
8use core::fmt::{Display, Formatter};
9use core::ops::{Add, BitAnd, BitOr, BitXor, Div, Mul, Neg as StdNeg, Not, Sub};
10use core::str::FromStr;
11use crate::concepts::DigitOperate;
12
13#[derive(Clone, PartialEq, Eq, Hash, Debug, Copy)]
38pub struct Tryte<const SIZE: usize = 6> {
39 raw: [Digit; SIZE],
41}
42
43impl<const SIZE: usize> Tryte<SIZE> {
44 pub const MAX: Self = Self::new([Pos; SIZE]);
46 pub const MIN: Self = Self::new([Neg; SIZE]);
48 pub const ZERO: Self = Self::new([Zero; SIZE]);
50
51 pub const fn new(digits: [Digit; SIZE]) -> Self {
75 if SIZE > 40 {
76 panic!("Cannot construct a Tryte with more than 40 digits (~63.5 bits).")
77 }
78 Self { raw: digits }
79 }
80
81 pub fn to_ternary(&self) -> Ternary {
87 Ternary::new(self.raw.to_vec())
88 }
89
90 pub const fn to_digit_slice(&self) -> &[Digit] {
99 &self.raw
100 }
101
102 pub fn from_ternary(v: &Ternary) -> Self {
112 if v.log() > SIZE {
113 panic!(
114 "Cannot convert a Ternary with more than {} digits to a Tryte<{}>.",
115 SIZE, SIZE
116 );
117 }
118 let mut digits = [Zero; SIZE];
119 for (i, d) in v.digits.iter().rev().enumerate() {
120 digits[SIZE - 1 - i] = *d;
121 }
122 Self::new(digits)
123 }
124
125 pub fn to_i64(&self) -> i64 {
131 self.to_ternary().to_dec()
132 }
133
134 pub fn from_i64(v: i64) -> Self {
144 Self::from_ternary(&Ternary::from_dec(v))
145 }
146
147}
148
149impl<const SIZE: usize> DigitOperate for Tryte<SIZE> {
150 fn to_digits(&self) -> Vec<Digit> {
151 self.to_digit_slice().to_vec()
152 }
153
154 fn digit(&self, index: usize) -> Option<Digit> {
164 if index > SIZE - 1 {
165 None
166 } else {
167 Some(*self.raw.iter().rev().nth(index).unwrap())
168 }
169 }
170
171 fn each(&self, f: impl Fn(Digit) -> Digit) -> Self {
173 Self::from_ternary(&self.to_ternary().each(f))
174 }
175
176 fn each_with(&self, f: impl Fn(Digit, Digit) -> Digit, with: Digit) -> Self {
178 Self::from_ternary(&self.to_ternary().each_with(f, with))
179 }
180
181 fn each_zip(&self, f: impl Fn(Digit, Digit) -> Digit, other: Self) -> Self {
183 Self::from_ternary(&self.to_ternary().each_zip(f, other.to_ternary()))
184 }
185
186 fn each_zip_carry(
188 &self,
189 f: impl Fn(Digit, Digit, Digit) -> (Digit, Digit),
190 other: Self,
191 ) -> Self {
192 Self::from_ternary(&self.to_ternary().each_zip_carry(f, other.to_ternary()))
193 }
194}
195
196
197impl<const SIZE: usize> Display for Tryte<SIZE> {
198 fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
203 write!(f, "{:01$}", self.to_ternary().to_string(), SIZE)
204 }
205}
206
207impl<const SIZE: usize> StdNeg for Tryte<SIZE> {
208 type Output = Tryte<SIZE>;
209 fn neg(self) -> Self::Output {
210 Self::from_ternary(&-&self.to_ternary())
211 }
212}
213
214impl<const SIZE: usize> Add for Tryte<SIZE> {
215 type Output = Tryte<SIZE>;
216
217 fn add(self, rhs: Self) -> Self::Output {
218 Self::from_ternary(&(&self.to_ternary() + &rhs.to_ternary()))
219 }
220}
221
222impl<const SIZE: usize> Sub for Tryte<SIZE> {
223 type Output = Tryte<SIZE>;
224
225 fn sub(self, rhs: Self) -> Self::Output {
226 Self::from_ternary(&(&self.to_ternary() - &rhs.to_ternary()))
227 }
228}
229
230impl<const SIZE: usize> Mul for Tryte<SIZE> {
231 type Output = Tryte<SIZE>;
232
233 fn mul(self, rhs: Self) -> Self::Output {
234 Self::from_ternary(&(&self.to_ternary() * &rhs.to_ternary()))
235 }
236}
237
238impl<const SIZE: usize> Div for Tryte<SIZE> {
239 type Output = Tryte<SIZE>;
240
241 fn div(self, rhs: Self) -> Self::Output {
242 Self::from_ternary(&(&self.to_ternary() / &rhs.to_ternary()))
243 }
244}
245
246impl<const SIZE: usize> BitAnd for Tryte<SIZE> {
247 type Output = Tryte<SIZE>;
248 fn bitand(self, rhs: Self) -> Self::Output {
249 Self::from_ternary(&(&self.to_ternary() & &rhs.to_ternary()))
250 }
251}
252
253impl<const SIZE: usize> BitOr for Tryte<SIZE> {
254 type Output = Tryte<SIZE>;
255 fn bitor(self, rhs: Self) -> Self::Output {
256 Self::from_ternary(&(&self.to_ternary() | &rhs.to_ternary()))
257 }
258}
259
260impl<const SIZE: usize> BitXor for Tryte<SIZE> {
261 type Output = Tryte<SIZE>;
262 fn bitxor(self, rhs: Self) -> Self::Output {
263 Self::from_ternary(&(&self.to_ternary() ^ &rhs.to_ternary()))
264 }
265}
266
267impl<const SIZE: usize> Not for Tryte<SIZE> {
268 type Output = Tryte<SIZE>;
269 fn not(self) -> Self::Output {
270 -self
271 }
272}
273
274impl<const SIZE: usize> From<Ternary> for Tryte<SIZE> {
275 fn from(value: Ternary) -> Self {
276 Tryte::from_ternary(&value)
277 }
278}
279
280impl<const SIZE: usize> From<Tryte<SIZE>> for Ternary {
281 fn from(value: Tryte<SIZE>) -> Self {
282 value.to_ternary()
283 }
284}
285
286impl<const SIZE: usize> From<&str> for Tryte<SIZE> {
287 fn from(value: &str) -> Self {
288 Self::from_ternary(&Ternary::parse(value))
289 }
290}
291
292impl<const SIZE: usize> From<String> for Tryte<SIZE> {
293 fn from(value: String) -> Self {
294 Self::from(value.as_str())
295 }
296}
297
298impl<const SIZE: usize> From<Tryte<SIZE>> for String {
299 fn from(value: Tryte<SIZE>) -> Self {
300 value.to_string()
301 }
302}
303
304impl<const SIZE: usize> From<i64> for Tryte<SIZE> {
305 fn from(value: i64) -> Self {
306 Self::from_i64(value)
307 }
308}
309
310impl<const SIZE: usize> From<Tryte<SIZE>> for i64 {
311 fn from(value: Tryte<SIZE>) -> Self {
312 value.to_i64()
313 }
314}
315
316impl<const SIZE: usize> FromStr for Tryte<SIZE> {
317 type Err = crate::ParseTernaryError;
318
319 fn from_str(s: &str) -> Result<Self, Self::Err> {
320 Ok(Tryte::from_ternary(&Ternary::from_str(s)?))
321 }
322}
323
324#[cfg(test)]
325#[test]
326pub fn test_tryte() {
327 let tryte = Tryte::<6>::from_i64(255);
328 assert_eq!(tryte.to_i64(), 255);
329 assert_eq!(tryte.to_string(), "+00++0");
330
331 let tryte = Tryte::<6>::from_i64(16);
332 assert_eq!(tryte.to_i64(), 16);
333 assert_eq!(tryte.to_string(), "00+--+");
334
335 assert_eq!(Tryte::<6>::MAX.to_string(), "++++++");
336 assert_eq!(Tryte::<6>::MAX.to_i64(), 364);
337 assert_eq!(Tryte::<6>::MIN.to_string(), "------");
338 assert_eq!(Tryte::<6>::MIN.to_i64(), -364);
339 assert_eq!(Tryte::<6>::ZERO.to_string(), "000000");
340 assert_eq!(Tryte::<6>::ZERO.to_i64(), 0);
341}
342
343#[cfg(test)]
344#[test]
345pub fn test_tryte_from_str() {
346 use core::str::FromStr;
347
348 let tryte = Tryte::<6>::from_str("+-0").unwrap();
349 assert_eq!(tryte.to_string(), "000+-0");
350
351 assert!(Tryte::<6>::from_str("+-x").is_err());
352}