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