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 fn inv(&self) -> Option<Self> {
236 self.decimal.inv().map(|d| SignedDecimal {
237 decimal: d,
238 negative: self.negative,
239 })
240 }
241}
242
243impl Div for SignedDecimal {
245 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
286pub fn decimal2uint128_floor(d: Decimal) -> Uint128 {
288 let base: u64 = 10; 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; 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; 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}