Skip to main content

ic_e8s/
c.rs

1use std::{
2    borrow::Cow,
3    cmp::Ordering,
4    fmt::Display,
5    ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign},
6};
7
8use candid::{CandidType, Nat};
9use ic_stable_structures::{storable::Bound, Storable};
10use num_bigint::BigUint;
11use serde::Deserialize;
12
13use crate::{d::EDs, ES_BASES};
14
15pub type E8s = ECs<8>;
16
17/// Fixed-point decimals with primitive math (+-*/) implemented correctly
18#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Default, Hash)]
19pub struct ECs<const DECIMALS: usize> {
20    pub val: BigUint,
21}
22
23impl<const D: usize> ECs<D> {
24    pub fn new(val: BigUint) -> Self {
25        Self { val }
26    }
27
28    pub fn base() -> &'static BigUint {
29        if D > 31 {
30            unreachable!("Decimal points after 31 are not supported");
31        }
32
33        // SAFETY: already checked
34        unsafe { ES_BASES.get(D).unwrap_unchecked() }
35    }
36
37    pub fn base_d(decimals: u8) -> &'static BigUint {
38        if decimals > 31 {
39            unreachable!("Decimal points after 31 are not supported");
40        }
41
42        // SAFETY: already checked
43        unsafe { ES_BASES.get(decimals as usize).unwrap_unchecked() }
44    }
45
46    pub fn zero() -> Self {
47        Self::new(BigUint::ZERO)
48    }
49
50    pub fn one() -> Self {
51        Self {
52            val: Self::base().clone(),
53        }
54    }
55
56    pub fn f0_1() -> Self {
57        Self::new(Self::base() / BigUint::from(10u64))
58    }
59
60    pub fn f0_2() -> Self {
61        Self::new(Self::base() / BigUint::from(5u64))
62    }
63
64    pub fn f0_25() -> Self {
65        Self::new(Self::base() / BigUint::from(4u64))
66    }
67
68    pub fn f0_3() -> Self {
69        Self::new(Self::base() * BigUint::from(3u64) / BigUint::from(10u64))
70    }
71
72    pub fn f0_33() -> Self {
73        Self::new(Self::base() / BigUint::from(3u64))
74    }
75
76    pub fn f0_4() -> Self {
77        Self::new(Self::base() * BigUint::from(2u64) / BigUint::from(5u64))
78    }
79
80    pub fn f0_5() -> Self {
81        Self::new(Self::base() / BigUint::from(2u64))
82    }
83
84    pub fn f0_6() -> Self {
85        Self::new(Self::base() * BigUint::from(3u64) / BigUint::from(5u64))
86    }
87
88    pub fn f0_67() -> Self {
89        Self::new(Self::base() * BigUint::from(2u64) / BigUint::from(3u64))
90    }
91
92    pub fn f0_7() -> Self {
93        Self::new(Self::base() * BigUint::from(7u64) / BigUint::from(10u64))
94    }
95
96    pub fn f0_75() -> Self {
97        Self::new(Self::base() * BigUint::from(3u64) / BigUint::from(4u64))
98    }
99
100    pub fn f0_8() -> Self {
101        Self::new(Self::base() * BigUint::from(4u64) / BigUint::from(5u64))
102    }
103
104    pub fn f0_9() -> Self {
105        Self::new(Self::base() * BigUint::from(9u64) / BigUint::from(10u64))
106    }
107
108    pub fn two() -> Self {
109        Self::new(Self::base() * BigUint::from(2u64))
110    }
111
112    pub fn to_dynamic(self) -> EDs {
113        EDs::new(self.val, D as u8)
114    }
115
116    pub fn to_decimals<const D1: usize>(self) -> ECs<D1> {
117        if D1 == D {
118            return ECs::<D1>::new(self.val);
119        }
120
121        let (dif, mul) = if D > D1 {
122            (D - D1, false)
123        } else {
124            (D1 - D, true)
125        };
126
127        let base = Self::base_d(dif as u8);
128
129        if mul {
130            ECs::<D1>::new(self.val * base)
131        } else {
132            ECs::<D1>::new(self.val / base)
133        }
134    }
135
136    pub fn sqrt(&self) -> Self {
137        let a = Self::one();
138        if self == &a {
139            return a;
140        }
141
142        let mut low = Self::zero();
143        let mut high = self.clone();
144        let one = BigUint::from(1u64);
145
146        while (&high - &low).val > one {
147            let mid = (&low + &high) / Self::two();
148            let mid_squared = &mid * &mid;
149
150            match mid_squared.cmp(self) {
151                Ordering::Equal => return mid,
152                Ordering::Greater => {
153                    high = mid;
154                }
155                Ordering::Less => {
156                    low = mid;
157                }
158            }
159        }
160
161        low
162    }
163}
164
165impl<const D: usize> Display for ECs<D> {
166    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
167        let base = ECs::<D>::base();
168
169        f.write_str(&format!("{}.{}", &self.val / base, &self.val % base))
170    }
171}
172
173impl<const D: usize> Add for &ECs<D> {
174    type Output = ECs<D>;
175
176    fn add(self, rhs: Self) -> Self::Output {
177        ECs::<D>::new(&self.val + &rhs.val)
178    }
179}
180
181impl<const D: usize> Add for ECs<D> {
182    type Output = ECs<D>;
183
184    fn add(self, rhs: Self) -> Self::Output {
185        (&self).add(&rhs)
186    }
187}
188
189impl<const D: usize> Add<&ECs<D>> for ECs<D> {
190    type Output = ECs<D>;
191
192    fn add(self, rhs: &ECs<D>) -> Self::Output {
193        (&self).add(rhs)
194    }
195}
196
197impl<const D: usize> Add<ECs<D>> for &ECs<D> {
198    type Output = ECs<D>;
199
200    fn add(self, rhs: ECs<D>) -> Self::Output {
201        self.add(&rhs)
202    }
203}
204
205impl<const D: usize> AddAssign<&ECs<D>> for ECs<D> {
206    fn add_assign(&mut self, rhs: &ECs<D>) {
207        self.val.add_assign(&rhs.val)
208    }
209}
210
211impl<const D: usize> AddAssign for ECs<D> {
212    fn add_assign(&mut self, rhs: Self) {
213        self.add_assign(&rhs)
214    }
215}
216
217impl<const D: usize> Sub for &ECs<D> {
218    type Output = ECs<D>;
219
220    fn sub(self, rhs: Self) -> Self::Output {
221        ECs::<D>::new(&self.val - &rhs.val)
222    }
223}
224
225impl<const D: usize> Sub for ECs<D> {
226    type Output = ECs<D>;
227
228    fn sub(self, rhs: Self) -> Self::Output {
229        (&self).sub(&rhs)
230    }
231}
232
233impl<const D: usize> Sub<&ECs<D>> for ECs<D> {
234    type Output = ECs<D>;
235
236    fn sub(self, rhs: &ECs<D>) -> Self::Output {
237        (&self).sub(rhs)
238    }
239}
240
241impl<const D: usize> Sub<ECs<D>> for &ECs<D> {
242    type Output = ECs<D>;
243
244    fn sub(self, rhs: ECs<D>) -> Self::Output {
245        self.sub(&rhs)
246    }
247}
248
249impl<const D: usize> SubAssign<&ECs<D>> for ECs<D> {
250    fn sub_assign(&mut self, rhs: &ECs<D>) {
251        self.val.sub_assign(&rhs.val)
252    }
253}
254
255impl<const D: usize> SubAssign for ECs<D> {
256    fn sub_assign(&mut self, rhs: Self) {
257        self.sub_assign(&rhs)
258    }
259}
260
261impl<const D: usize> Mul for &ECs<D> {
262    type Output = ECs<D>;
263
264    fn mul(self, rhs: Self) -> Self::Output {
265        ECs::<D>::new(&self.val * &rhs.val / ECs::<D>::base())
266    }
267}
268
269impl<const D: usize> Mul for ECs<D> {
270    type Output = ECs<D>;
271
272    fn mul(self, rhs: Self) -> Self::Output {
273        (&self).mul(&rhs)
274    }
275}
276
277impl<const D: usize> Mul<&ECs<D>> for ECs<D> {
278    type Output = ECs<D>;
279
280    fn mul(self, rhs: &ECs<D>) -> Self::Output {
281        (&self).mul(rhs)
282    }
283}
284
285impl<const D: usize> Mul<ECs<D>> for &ECs<D> {
286    type Output = ECs<D>;
287
288    fn mul(self, rhs: ECs<D>) -> Self::Output {
289        self.mul(&rhs)
290    }
291}
292
293impl<const D: usize> MulAssign<&ECs<D>> for ECs<D> {
294    fn mul_assign(&mut self, rhs: &ECs<D>) {
295        self.val = &self.val * &rhs.val / ECs::<D>::base()
296    }
297}
298
299impl<const D: usize> MulAssign for ECs<D> {
300    fn mul_assign(&mut self, rhs: Self) {
301        self.mul_assign(&rhs)
302    }
303}
304
305impl<const D: usize> Div for &ECs<D> {
306    type Output = ECs<D>;
307
308    fn div(self, rhs: Self) -> Self::Output {
309        ECs::<D>::new(&self.val * ECs::<D>::base() / &rhs.val)
310    }
311}
312
313impl<const D: usize> Div for ECs<D> {
314    type Output = ECs<D>;
315
316    fn div(self, rhs: Self) -> Self::Output {
317        (&self).div(&rhs)
318    }
319}
320
321impl<const D: usize> Div<&ECs<D>> for ECs<D> {
322    type Output = ECs<D>;
323
324    fn div(self, rhs: &ECs<D>) -> Self::Output {
325        (&self).div(rhs)
326    }
327}
328
329impl<const D: usize> Div<ECs<D>> for &ECs<D> {
330    type Output = ECs<D>;
331
332    fn div(self, rhs: ECs<D>) -> Self::Output {
333        self.div(&rhs)
334    }
335}
336
337impl<const D: usize> DivAssign<&ECs<D>> for ECs<D> {
338    fn div_assign(&mut self, rhs: &ECs<D>) {
339        self.val = &self.val * ECs::<D>::base() / &rhs.val;
340    }
341}
342
343impl<const D: usize> DivAssign for ECs<D> {
344    fn div_assign(&mut self, rhs: Self) {
345        self.div_assign(&rhs)
346    }
347}
348
349impl<const D: usize> CandidType for ECs<D> {
350    fn _ty() -> candid::types::Type {
351        Nat::_ty()
352    }
353
354    fn idl_serialize<S>(&self, serializer: S) -> Result<(), S::Error>
355    where
356        S: candid::types::Serializer,
357    {
358        Nat::idl_serialize(&Nat(self.val.clone()), serializer)
359    }
360}
361
362impl<'de, const C: usize> Deserialize<'de> for ECs<C> {
363    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
364    where
365        D: serde::Deserializer<'de>,
366    {
367        Ok(ECs::new(Nat::deserialize(deserializer)?.0))
368    }
369}
370
371impl<const D: usize> From<u64> for ECs<D> {
372    fn from(value: u64) -> Self {
373        Self::new(BigUint::from(value))
374    }
375}
376
377impl<const D: usize> From<u128> for ECs<D> {
378    fn from(value: u128) -> Self {
379        Self::new(BigUint::from(value))
380    }
381}
382
383impl Storable for E8s {
384    fn to_bytes(&self) -> std::borrow::Cow<[u8]> {
385        let mut val_buf = self.val.to_bytes_le();
386        let len = val_buf.len();
387
388        assert!(len <= 32, "Unable to encode E8s: value too big");
389
390        val_buf.resize(33, 0);
391        val_buf[32] = len as u8;
392
393        std::borrow::Cow::Owned(val_buf)
394    }
395
396    fn from_bytes(bytes: std::borrow::Cow<[u8]>) -> Self {
397        assert_eq!(
398            bytes.len(),
399            33,
400            "Unable to decode E8s: invalid number of bytes provider"
401        );
402
403        let len = bytes[32];
404        let val = BigUint::from_bytes_le(&bytes[0..len as usize]);
405
406        Self { val }
407    }
408
409    const BOUND: Bound = Bound::Bounded {
410        max_size: 33,
411        is_fixed_size: true,
412    };
413}
414
415#[cfg(test)]
416mod tests {
417    use ic_stable_structures::Storable;
418
419    use crate::c::E8s;
420
421    #[test]
422    fn encoding_works_fine() {
423        let a = E8s::f0_2();
424        let a1 = E8s::from_bytes(a.to_bytes());
425        assert_eq!(a, a1);
426
427        let b = E8s::one();
428        let b1 = E8s::from_bytes(b.to_bytes());
429        assert_eq!(b, b1);
430
431        let c = E8s::from(u128::MAX);
432        let c1 = E8s::from_bytes(c.to_bytes());
433        assert_eq!(c, c1);
434
435        let d = E8s::from(u64::MAX);
436        let d1 = E8s::from_bytes(d.to_bytes());
437        assert_eq!(d, d1);
438    }
439
440    #[test]
441    fn sqrt_works_fine() {
442        assert_eq!(E8s::zero().sqrt(), E8s::zero());
443        assert_eq!(E8s::one().sqrt(), E8s::one());
444        assert_eq!(E8s::from(4_0000_0000u64).sqrt(), E8s::two());
445        assert_eq!(
446            E8s::from(100_0000_0000u64).sqrt(),
447            E8s::from(10_0000_0000u64)
448        );
449        assert_eq!(E8s::two().sqrt(), E8s::from(1_4142_1356u64));
450    }
451}