crypto/encoding/ternary/
convert.rs1use core::{cmp::PartialOrd, convert::TryFrom};
5
6use num_traits::{AsPrimitive, CheckedAdd, CheckedSub, FromPrimitive, Num, Signed};
7
8use crate::encoding::ternary::{Btrit, RawEncoding, RawEncodingBuf, Trit, TritBuf, Trits, Utrit};
9
10#[derive(Debug, Eq, PartialEq)]
12pub enum Error {
13 Empty,
15 Overflow,
17 Underflow,
19}
20
21macro_rules! signed_try_from_trits {
33 ($int:ident) => {
34 impl<'a, T: RawEncoding<Trit = Btrit> + ?Sized> TryFrom<&'a Trits<T>> for $int {
35 type Error = Error;
36 fn try_from(trits: &'a Trits<T>) -> Result<Self, Self::Error> {
37 trits_to_int(trits)
38 }
39 }
40
41 impl<T: RawEncodingBuf> From<$int> for TritBuf<T>
42 where
43 T::Slice: RawEncoding<Trit = Btrit>,
44 {
45 fn from(x: $int) -> Self {
46 signed_int_trits(x).collect()
47 }
48 }
49 };
50}
51
52#[cfg(has_i128)]
56signed_try_from_trits!(i128);
57signed_try_from_trits!(i64);
58signed_try_from_trits!(i32);
59signed_try_from_trits!(i16);
60signed_try_from_trits!(i8);
61
62macro_rules! unsigned_try_from_trits {
63 ($int:ident) => {
64 impl<'a, T: RawEncoding<Trit = Utrit> + ?Sized> TryFrom<&'a Trits<T>> for $int {
65 type Error = Error;
66 fn try_from(trits: &'a Trits<T>) -> Result<Self, Self::Error> {
67 trits_to_int(trits)
68 }
69 }
70
71 impl<T: RawEncodingBuf> From<$int> for TritBuf<T>
72 where
73 T::Slice: RawEncoding<Trit = Utrit>,
74 {
75 fn from(x: $int) -> Self {
76 unsigned_int_trits(x).collect()
77 }
78 }
79 };
80}
81
82#[cfg(has_u128)]
86unsigned_try_from_trits!(u128);
87unsigned_try_from_trits!(u64);
88unsigned_try_from_trits!(u32);
89unsigned_try_from_trits!(u16);
90unsigned_try_from_trits!(u8);
91
92pub fn trits_to_int<I, T: RawEncoding + ?Sized>(trits: &Trits<T>) -> Result<I, Error>
96where
97 I: Clone + CheckedAdd + CheckedSub + PartialOrd + Num,
98{
99 if trits.is_empty() {
100 Err(Error::Empty)
101 } else {
102 let mut acc = I::zero();
103
104 for trit in trits.iter().rev() {
105 let old_acc = acc.clone();
106 acc = trit
107 .add_to_num(acc)?
108 .checked_add(&old_acc)
109 .and_then(|acc| acc.checked_add(&old_acc))
110 .ok_or_else(|| {
111 if old_acc < I::zero() {
112 Error::Underflow
113 } else {
114 Error::Overflow
115 }
116 })?;
117 }
118
119 Ok(acc)
120 }
121}
122
123pub fn signed_int_trits<I>(x: I) -> impl Iterator<Item = Btrit> + Clone
125where
126 I: Clone + AsPrimitive<i8> + FromPrimitive + Signed,
127{
128 let is_neg = x.is_negative();
129 let mut x = if is_neg { x } else { -x };
130
131 let radix = I::from_i8(3).unwrap();
132
133 core::iter::from_fn(move || {
134 if x.is_zero() {
135 None
136 } else {
137 let modulus = ((x + I::one()).abs() % radix).as_();
138 x = x / radix;
139 if modulus == 1 {
140 x = x + -I::one();
141 }
142 Some(Btrit::try_from(((modulus + 2) % 3 - 1) * if is_neg { -1 } else { 1 }).unwrap())
143 }
144 })
145 .chain(Some(Btrit::Zero).filter(|_| x.is_zero()))
147}
148
149pub fn unsigned_int_trits<I>(mut x: I) -> impl Iterator<Item = Utrit> + Clone
151where
152 I: Clone + AsPrimitive<u8> + FromPrimitive + Num,
153{
154 let radix = I::from_u8(3).unwrap();
155
156 core::iter::from_fn(move || {
157 if x.is_zero() {
158 None
159 } else {
160 let modulus = (x % radix).as_();
161 x = x / radix;
162 Some(Utrit::try_from(modulus).unwrap())
163 }
164 })
165 .chain(Some(Utrit::Zero).filter(|_| x.is_zero()))
167}