Skip to main content

ic_e8s/
d.rs

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