vortex_common/
utils.rs

1use cosmwasm_std::{Decimal, DecimalRangeExceeded, Fraction, Uint128};
2use forward_ref::{forward_ref_binop, forward_ref_op_assign};
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5use std::cmp::Ordering;
6use std::ops::{Add, AddAssign, Div, Mul, Sub, SubAssign};
7use std::{fmt, ops::BitXor};
8
9#[derive(Copy, Clone, Serialize, Deserialize, PartialEq, JsonSchema, Debug, Eq)]
10pub struct SignedDecimal {
11    pub decimal: Decimal,
12    pub negative: bool,
13}
14
15impl SignedDecimal {
16    pub const fn zero() -> Self {
17        SignedDecimal {
18            decimal: Decimal::zero(),
19            negative: false,
20        }
21    }
22    pub const fn one() -> Self {
23        SignedDecimal {
24            decimal: Decimal::one(),
25            negative: false,
26        }
27    }
28
29    pub const fn new(decimal: Decimal) -> Self {
30        SignedDecimal {
31            decimal: decimal,
32            negative: false,
33        }
34    }
35
36    pub const fn new_from_ptr(decimal: &Decimal) -> Self {
37        SignedDecimal {
38            decimal: *decimal,
39            negative: false,
40        }
41    }
42
43    pub const fn new_negative(decimal: Decimal) -> Self {
44        SignedDecimal {
45            decimal: decimal,
46            negative: true,
47        }
48    }
49
50    pub const fn new_signed(decimal: Decimal, negative: bool) -> Self {
51        SignedDecimal {
52            decimal: decimal,
53            negative: negative,
54        }
55    }
56
57    pub fn from_atomics(
58        atomics: impl Into<Uint128>,
59        decimal_places: u32,
60        negative: bool,
61    ) -> Result<Self, DecimalRangeExceeded> {
62        match Decimal::from_atomics(atomics, decimal_places) {
63            Ok(decimal) => Result::Ok(SignedDecimal {
64                decimal: decimal,
65                negative: negative,
66            }),
67            Err(err) => Result::Err(err),
68        }
69    }
70
71    pub fn negation(&self) -> Self {
72        if self.decimal == Decimal::zero() {
73            return *self;
74        }
75        return SignedDecimal {
76            decimal: self.decimal,
77            negative: !self.negative,
78        };
79    }
80
81    pub fn is_zero(&self) -> bool {
82        self.decimal == Decimal::zero()
83    }
84
85    pub fn positive_part(&self) -> SignedDecimal {
86        if self.negative {
87            return SignedDecimal::zero();
88        }
89        *self
90    }
91}
92
93impl Ord for SignedDecimal {
94    fn cmp(&self, other: &SignedDecimal) -> Ordering {
95        if self.negative && other.negative {
96            if self.decimal > other.decimal {
97                Ordering::Less
98            } else if self.decimal == other.decimal {
99                Ordering::Equal
100            } else {
101                Ordering::Greater
102            }
103        } else if !self.negative && !other.negative {
104            if self.decimal < other.decimal {
105                Ordering::Less
106            } else if self.decimal == other.decimal {
107                Ordering::Equal
108            } else {
109                Ordering::Greater
110            }
111        } else if !self.negative && other.negative {
112            Ordering::Greater
113        } else {
114            Ordering::Less
115        }
116    }
117}
118
119impl PartialOrd for SignedDecimal {
120    fn partial_cmp(&self, other: &SignedDecimal) -> Option<Ordering> {
121        Some(self.cmp(other))
122    }
123}
124
125impl Add for SignedDecimal {
126    type Output = Self;
127
128    fn add(self, other: Self) -> Self {
129        if self.negative && other.negative {
130            SignedDecimal {
131                decimal: self.decimal + other.decimal,
132                negative: true,
133            }
134        } else if self.negative && !other.negative {
135            if self.decimal > other.decimal {
136                SignedDecimal {
137                    decimal: self.decimal - other.decimal,
138                    negative: true,
139                }
140            } else {
141                SignedDecimal {
142                    decimal: other.decimal - self.decimal,
143                    negative: false,
144                }
145            }
146        } else if !self.negative && other.negative {
147            if self.decimal >= other.decimal {
148                SignedDecimal {
149                    decimal: self.decimal - other.decimal,
150                    negative: false,
151                }
152            } else {
153                SignedDecimal {
154                    decimal: other.decimal - self.decimal,
155                    negative: true,
156                }
157            }
158        } else {
159            assert_eq!(!self.negative && !other.negative, true);
160            SignedDecimal {
161                decimal: self.decimal + other.decimal,
162                negative: false,
163            }
164        }
165    }
166}
167forward_ref_binop!(impl Add, add for SignedDecimal, SignedDecimal);
168
169impl AddAssign for SignedDecimal {
170    fn add_assign(&mut self, rhs: SignedDecimal) {
171        *self = *self + rhs;
172    }
173}
174forward_ref_op_assign!(impl AddAssign, add_assign for SignedDecimal, SignedDecimal);
175
176impl Sub for SignedDecimal {
177    type Output = Self;
178
179    fn sub(self, other: Self) -> Self {
180        if other.decimal == Decimal::zero() {
181            return self;
182        }
183        self + SignedDecimal {
184            decimal: other.decimal,
185            negative: !other.negative,
186        }
187    }
188}
189forward_ref_binop!(impl Sub, sub for SignedDecimal, SignedDecimal);
190
191impl SubAssign for SignedDecimal {
192    fn sub_assign(&mut self, rhs: SignedDecimal) {
193        *self = *self - rhs;
194    }
195}
196forward_ref_op_assign!(impl SubAssign, sub_assign for SignedDecimal, SignedDecimal);
197
198impl Mul for SignedDecimal {
199    type Output = Self;
200
201    #[allow(clippy::suspicious_arithmetic_impl)]
202    fn mul(self, other: Self) -> Self {
203        if (self.negative && other.negative) || (!self.negative && !other.negative) {
204            SignedDecimal {
205                decimal: self.decimal * other.decimal,
206                negative: false,
207            }
208        } else {
209            let mut is_result_negative = true;
210            if self.decimal == Decimal::zero() || other.decimal == Decimal::zero() {
211                is_result_negative = false
212            }
213            SignedDecimal {
214                decimal: self.decimal * other.decimal,
215                negative: is_result_negative,
216            }
217        }
218    }
219}
220
221impl Fraction<Uint128> for SignedDecimal {
222    #[inline]
223    fn numerator(&self) -> Uint128 {
224        self.decimal.numerator()
225    }
226
227    #[inline]
228    fn denominator(&self) -> Uint128 {
229        self.decimal.denominator()
230    }
231
232    /// Returns the multiplicative inverse `1/d` for decimal `d`.
233    ///
234    /// If `d` is zero, none is returned.
235    fn inv(&self) -> Option<Self> {
236        self.decimal.inv().map(|d| SignedDecimal {
237            decimal: d,
238            negative: self.negative,
239        })
240    }
241}
242
243/// SignedDecimal / SignedDecimal
244impl Div for SignedDecimal {
245    // The division of signeddecimal is a closed operation.
246    type Output = Self;
247
248    fn div(self, rhs: Self) -> Self::Output {
249        if rhs.decimal.is_zero() {
250            panic!("Cannot divide by zero-valued `SignedDecimal`!");
251        }
252        let reciprocal = rhs.decimal.inv().unwrap();
253        let decimal_res = match reciprocal.checked_mul(self.decimal) {
254            Ok(res) => res,
255            Err(e) => panic!("{}", e),
256        };
257        match self.negative.bitxor(rhs.negative) {
258            true => Self::new_negative(decimal_res),
259            false => Self::new(decimal_res),
260        }
261    }
262}
263
264impl fmt::Display for SignedDecimal {
265    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
266        if self.negative {
267            write!(f, "-{}", self.decimal)
268        } else {
269            write!(f, "{}", self.decimal)
270        }
271    }
272}
273
274fn epsilon() -> Decimal {
275    Decimal::from_atomics(1u128, 8).unwrap()
276}
277
278pub fn roughly_equal(d1: Decimal, d2: Decimal) -> bool {
279    roughly_equal_signed(SignedDecimal::new(d1), SignedDecimal::new(d2))
280}
281
282pub fn roughly_equal_signed(d1: SignedDecimal, d2: SignedDecimal) -> bool {
283    (d1 - d2).decimal < epsilon()
284}
285
286// convert decimal to uint128, conservative round down
287pub fn decimal2uint128_floor(d: Decimal) -> Uint128 {
288    let base: u64 = 10; // to avoid overflow with 10^18
289    let atomics = d.atomics();
290    let decimal_places = d.decimal_places();
291    atomics / Uint128::new(base.pow(decimal_places) as u128)
292}
293
294pub fn decimal2u128_floor(d: Decimal) -> u128 {
295    let base: u64 = 10; // to avoid overflow with 10^18
296    let atomics = d.atomics();
297    let decimal_places = d.decimal_places();
298    atomics.u128() / base.pow(decimal_places) as u128
299}
300
301pub fn decimal2u128_ceiling(d: Decimal) -> u128 {
302    let base: u64 = 10; // to avoid overflow with 10^18
303    let atomics = d.atomics();
304    let decimal_places = d.decimal_places();
305    let divisor = base.pow(decimal_places) as u128;
306    (atomics.u128() + divisor - 1) / divisor
307}