1use crate::decimal::{DEFAULT_DIVISION_SCALE, Decimal, MAX_SUPPORTED_SCALE};
2use ethnum::I256;
3use std::{
4 iter::{Product, Sum},
5 ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Neg, Rem, RemAssign, Sub, SubAssign},
6};
7
8impl Decimal {
9 fn checked_add_impl(self, rhs: Self) -> Option<Self> {
10 let target_scale = self.scale.max(rhs.scale);
11 let lhs = Self::align_to_scale(self.mantissa, self.scale, target_scale)?;
12 let rhs = Self::align_to_scale(rhs.mantissa, rhs.scale, target_scale)?;
13
14 Self::fit_wide_mantissa(lhs.checked_add(rhs)?, target_scale)
15 }
16
17 #[must_use]
21 pub fn checked_add(self, rhs: Self) -> Option<Self> {
22 self.checked_add_impl(rhs)
23 }
24
25 #[must_use]
28 pub fn checked_sub(self, rhs: Self) -> Option<Self> {
29 let target_scale = self.scale.max(rhs.scale);
30 let lhs = Self::align_to_scale(self.mantissa, self.scale, target_scale)?;
31 let rhs = Self::align_to_scale(rhs.mantissa, rhs.scale, target_scale)?;
32 Self::fit_wide_mantissa(lhs.checked_sub(rhs)?, target_scale)
33 }
34
35 #[must_use]
39 pub fn checked_mul(self, rhs: Self) -> Option<Self> {
40 self.checked_mul_impl(rhs)
41 }
42
43 #[must_use]
47 pub fn checked_div(self, rhs: Self) -> Option<Self> {
48 self.checked_div_impl(rhs)
49 }
50
51 fn checked_mul_impl(self, rhs: Self) -> Option<Self> {
52 let lhs = self.normalize();
55 let rhs = rhs.normalize();
56 let scale = lhs.scale.checked_add(rhs.scale)?;
57 let product = I256::from(lhs.mantissa).checked_mul(I256::from(rhs.mantissa))?;
60 Self::fit_wide_mantissa(product, scale)
61 }
62
63 fn fit_wide_mantissa(mantissa: I256, scale: u32) -> Option<Self> {
66 let mut result_scale = scale.min(MAX_SUPPORTED_SCALE);
67 let mut divisor = I256::new(10).checked_pow(scale - result_scale)?;
68 loop {
69 if let Some(mantissa) = Self::div_round_half_away_from_zero(mantissa, divisor) {
70 return Some(Self {
71 mantissa,
72 scale: result_scale,
73 });
74 }
75 if result_scale == 0 {
76 return None;
77 }
78 result_scale -= 1;
79 divisor = divisor.checked_mul(I256::new(10))?;
80 }
81 }
82
83 fn checked_div_impl(self, rhs: Self) -> Option<Self> {
84 if rhs.is_zero() {
85 return None;
86 }
87
88 let lhs = self.normalize();
89 let rhs = rhs.normalize();
90 let mut target_scale = DEFAULT_DIVISION_SCALE;
91
92 loop {
96 if let Some((numerator, denominator)) = Self::division_operands(lhs, rhs, target_scale)
97 && let Some(mantissa) = Self::div_round_half_away_from_zero(numerator, denominator)
98 {
99 return Some(
100 Self {
101 mantissa,
102 scale: target_scale,
103 }
104 .normalize(),
105 );
106 }
107
108 if target_scale == 0 {
109 return None;
110 }
111
112 target_scale -= 1;
113 }
114 }
115
116 fn checked_rem_impl(self, rhs: Self) -> Option<Self> {
117 if rhs.is_zero() {
118 return None;
119 }
120
121 let target_scale = self.scale.max(rhs.scale);
122 let lhs = Self::align_to_scale(self.mantissa, self.scale, target_scale)?;
126 let rhs = Self::align_to_scale(rhs.mantissa, rhs.scale, target_scale)?;
127
128 Some(Self {
129 mantissa: i128::try_from(lhs.checked_rem(rhs)?).ok()?,
130 scale: target_scale,
131 })
132 }
133
134 #[must_use]
136 pub const fn round_dp(&self, dp: u32) -> Self {
137 if self.scale <= dp {
138 return *self;
139 }
140
141 let diff = self.scale - dp;
142 let Some(divisor) = Self::checked_pow10(diff) else {
143 return *self;
144 };
145 let quotient = self.mantissa / divisor;
146 let remainder = self.mantissa % divisor;
147
148 let should_round = remainder.unsigned_abs() >= divisor.unsigned_abs() / 2;
150 let rounded = if should_round {
151 if self.mantissa.is_negative() {
152 quotient.saturating_sub(1)
153 } else {
154 quotient.saturating_add(1)
155 }
156 } else {
157 quotient
158 };
159
160 Self {
161 mantissa: rounded,
162 scale: dp,
163 }
164 }
165
166 #[must_use]
168 pub const fn trunc_dp(&self, dp: u32) -> Self {
169 if self.scale <= dp {
170 return *self;
171 }
172
173 let diff = self.scale - dp;
174 let Some(divisor) = Self::checked_pow10(diff) else {
175 return *self;
176 };
177
178 Self {
179 mantissa: self.mantissa / divisor,
180 scale: dp,
181 }
182 }
183
184 #[must_use]
186 pub const fn abs(&self) -> Self {
187 Self {
188 mantissa: self.mantissa.saturating_abs(),
189 scale: self.scale,
190 }
191 }
192
193 #[must_use]
195 pub const fn floor_dp0(&self) -> Self {
196 if self.scale == 0 {
197 return *self;
198 }
199
200 let Some(divisor) = Self::checked_pow10(self.scale) else {
201 return *self;
202 };
203 let quotient = self.mantissa / divisor;
204 let remainder = self.mantissa % divisor;
205 let integer = if self.mantissa.is_negative() && remainder != 0 {
206 quotient.saturating_sub(1)
207 } else {
208 quotient
209 };
210
211 Self {
212 mantissa: integer,
213 scale: 0,
214 }
215 }
216
217 #[must_use]
219 pub const fn ceil_dp0(&self) -> Self {
220 if self.scale == 0 {
221 return *self;
222 }
223
224 let Some(divisor) = Self::checked_pow10(self.scale) else {
225 return *self;
226 };
227 let quotient = self.mantissa / divisor;
228 let remainder = self.mantissa % divisor;
229 let integer = if self.mantissa.is_positive() && remainder != 0 {
230 quotient.saturating_add(1)
231 } else {
232 quotient
233 };
234
235 Self {
236 mantissa: integer,
237 scale: 0,
238 }
239 }
240
241 #[must_use]
243 pub fn saturating_add(self, rhs: Self) -> Self {
244 self.checked_add_impl(rhs)
247 .unwrap_or_else(|| Self::saturating_extreme(self.is_sign_negative()))
248 }
249
250 #[must_use]
252 pub fn saturating_sub(self, rhs: Self) -> Self {
253 self.checked_sub(rhs)
256 .unwrap_or_else(|| Self::saturating_extreme(self < rhs))
257 }
258
259 #[must_use]
262 pub fn checked_rem(self, rhs: Self) -> Option<Self> {
263 self.checked_rem_impl(rhs)
264 }
265
266 #[must_use]
269 pub const fn checked_abs(&self) -> Option<Self> {
270 let Some(mantissa) = self.mantissa.checked_abs() else {
271 return None;
272 };
273
274 Some(Self {
275 mantissa,
276 scale: self.scale,
277 })
278 }
279
280 #[must_use]
282 pub fn powu(&self, exp: u64) -> Self {
283 if exp == 0 {
284 return Self::new(1, 0);
285 }
286
287 let mut base = *self;
288 let mut power = exp;
289 let mut acc = Self::new(1, 0);
290
291 while power > 0 {
292 if power & 1 == 1 {
293 acc *= base;
294 }
295
296 power >>= 1;
297
298 if power > 0 {
299 base = base * base;
300 }
301 }
302
303 acc
304 }
305
306 #[must_use]
311 pub fn checked_powu(&self, exp: u64) -> Option<Self> {
312 if exp == 0 {
313 return Some(Self::new(1, 0));
314 }
315
316 let mut base = *self;
317 let mut power = exp;
318 let mut acc = Self::new(1, 0);
319
320 while power > 0 {
321 if power & 1 == 1 {
322 acc = acc.checked_mul(base)?;
323 }
324
325 power >>= 1;
326
327 if power > 0 {
328 base = base.checked_mul(base)?;
329 }
330 }
331
332 Some(acc)
333 }
334
335 fn align_to_scale(mantissa: i128, current_scale: u32, target_scale: u32) -> Option<I256> {
338 let factor = Self::checked_pow10(target_scale.checked_sub(current_scale)?)?;
339 I256::from(mantissa).checked_mul(I256::from(factor))
340 }
341
342 fn division_operands(lhs: Self, rhs: Self, target_scale: u32) -> Option<(I256, I256)> {
344 let exponent = i64::from(target_scale) + i64::from(rhs.scale) - i64::from(lhs.scale);
345 let factor = I256::new(10).checked_pow(u32::try_from(exponent.unsigned_abs()).ok()?)?;
346 let lhs = I256::from(lhs.mantissa);
347 let rhs = I256::from(rhs.mantissa);
348
349 if exponent >= 0 {
350 return Some((lhs.checked_mul(factor)?, rhs));
351 }
352
353 Some((lhs, rhs.checked_mul(factor)?))
354 }
355
356 fn div_round_half_away_from_zero(numerator: I256, denominator: I256) -> Option<i128> {
358 let quotient = numerator.checked_div(denominator)?;
361 let remainder = numerator.checked_rem(denominator)?;
362
363 if remainder == 0 {
364 return i128::try_from(quotient).ok();
365 }
366
367 let twice_remainder = remainder.unsigned_abs().checked_mul(2_u8.into())?;
368 if twice_remainder < denominator.unsigned_abs() {
369 return i128::try_from(quotient).ok();
370 }
371
372 let rounded = if (numerator < 0) == (denominator < 0) {
373 quotient.checked_add(I256::new(1))?
374 } else {
375 quotient.checked_sub(I256::new(1))?
376 };
377 i128::try_from(rounded).ok()
378 }
379}
380
381impl Add for Decimal {
382 type Output = Self;
383
384 fn add(self, rhs: Self) -> Self::Output {
385 self.saturating_add(rhs)
386 }
387}
388
389impl AddAssign for Decimal {
390 fn add_assign(&mut self, rhs: Self) {
391 *self = *self + rhs;
392 }
393}
394
395impl Sub for Decimal {
396 type Output = Self;
397
398 fn sub(self, rhs: Self) -> Self::Output {
399 self.saturating_sub(rhs)
400 }
401}
402
403impl SubAssign for Decimal {
404 fn sub_assign(&mut self, rhs: Self) {
405 *self = *self - rhs;
406 }
407}
408
409impl Mul for Decimal {
410 type Output = Self;
411
412 fn mul(self, rhs: Self) -> Self::Output {
413 self.checked_mul_impl(rhs).unwrap_or_else(|| {
414 Self::saturating_extreme(self.is_sign_negative() != rhs.is_sign_negative())
415 })
416 }
417}
418
419impl MulAssign for Decimal {
420 fn mul_assign(&mut self, rhs: Self) {
421 *self = *self * rhs;
422 }
423}
424
425impl Neg for Decimal {
426 type Output = Self;
427
428 fn neg(self) -> Self::Output {
429 Self {
430 mantissa: self.mantissa.saturating_neg(),
431 scale: self.scale,
432 }
433 }
434}
435
436impl Product for Decimal {
437 fn product<I: Iterator<Item = Self>>(iter: I) -> Self {
438 iter.fold(Self::new_unchecked(1, 0), |acc, value| acc * value)
439 }
440}
441
442impl Div for Decimal {
443 type Output = Self;
444
445 fn div(self, rhs: Self) -> Self::Output {
446 if rhs.is_zero() {
447 return Self::ZERO;
448 }
449
450 self.checked_div_impl(rhs).unwrap_or_else(|| {
451 let negative = self.is_sign_negative() != rhs.is_sign_negative();
452 Self::saturating_extreme(negative)
453 })
454 }
455}
456
457impl DivAssign for Decimal {
458 fn div_assign(&mut self, rhs: Self) {
459 *self = *self / rhs;
460 }
461}
462
463impl Rem for Decimal {
464 type Output = Self;
465
466 fn rem(self, rhs: Self) -> Self::Output {
467 self.checked_rem_impl(rhs).unwrap_or(Self::ZERO)
468 }
469}
470
471impl RemAssign for Decimal {
472 fn rem_assign(&mut self, rhs: Self) {
473 *self = *self % rhs;
474 }
475}
476
477impl Sum for Decimal {
478 fn sum<I: Iterator<Item = Self>>(iter: I) -> Self {
479 iter.fold(Self::ZERO, |acc, value| acc + value)
480 }
481}