bigdecimal/arithmetic/
addition.rs

1//! addition routines
2//!
3
4use crate::*;
5
6
7pub(crate) fn add_bigdecimals(
8    mut a: BigDecimal,
9    mut b: BigDecimal,
10) -> BigDecimal {
11    if b.is_zero() {
12        a.extend_scale_to(b.scale);
13        return a;
14    }
15
16    if a.is_zero() {
17        b.extend_scale_to(a.scale);
18        return b;
19    }
20
21    let (a, b) = match a.scale.cmp(&b.scale) {
22        Ordering::Equal => (a, b),
23        Ordering::Less => (a.take_and_scale(b.scale), b),
24        Ordering::Greater => (b.take_and_scale(a.scale), a),
25    };
26
27    add_aligned_bigdecimals(a, b)
28}
29
30fn add_aligned_bigdecimals(
31    mut a: BigDecimal,
32    mut b: BigDecimal,
33) -> BigDecimal {
34    debug_assert_eq!(a.scale, b.scale);
35    if a.int_val.bits() >= b.int_val.bits() {
36        a.int_val += b.int_val;
37        a
38    } else {
39        b.int_val += a.int_val;
40        b
41    }
42}
43
44
45pub(crate) fn add_bigdecimal_refs<'a, 'b, Lhs, Rhs>(
46    lhs: Lhs,
47    rhs: Rhs,
48    ctx: Option<&Context>,
49) -> BigDecimal
50where
51    Rhs: Into<BigDecimalRef<'a>>,
52    Lhs: Into<BigDecimalRef<'b>>,
53{
54    use stdlib::cmp::Ordering::*;
55
56    let lhs = lhs.into();
57    let rhs = rhs.into();
58    if rhs.is_zero() {
59        let scale_diff = rhs.scale.saturating_sub(lhs.scale).max(0).min(15);
60        return lhs.to_owned_with_scale(lhs.scale + scale_diff);
61    }
62    if lhs.is_zero() {
63        let scale_diff = lhs.scale.saturating_sub(rhs.scale).max(0).min(15);
64        return rhs.to_owned_with_scale(rhs.scale + scale_diff);
65    }
66
67    match lhs.scale.cmp(&rhs.scale) {
68        Equal => {
69            add_aligned_bigdecimal_ref_ref(lhs, rhs)
70        }
71        Greater => {
72            add_unaligned_bigdecimal_ref_ref(lhs, rhs, ctx)
73        }
74        Less => {
75            add_unaligned_bigdecimal_ref_ref(rhs, lhs, ctx)
76        }
77    }
78}
79
80
81pub(crate) fn addassign_bigdecimals(
82    lhs: &mut BigDecimal,
83    rhs: BigDecimal,
84) {
85    if rhs.is_zero() {
86        return;
87    }
88    if lhs.is_zero() {
89        *lhs = rhs;
90        return;
91    }
92    lhs.add_assign(rhs.to_ref());
93}
94
95
96pub(crate) fn addassign_bigdecimal_ref<'a, T: Into<BigDecimalRef<'a>>>(
97    lhs: &mut BigDecimal,
98    rhs: T,
99) {
100    // TODO: Replace to_owned() with efficient addition algorithm
101    let rhs = rhs.into().to_owned();
102    match lhs.scale.cmp(&rhs.scale) {
103        Ordering::Less => {
104            let scaled = lhs.with_scale(rhs.scale);
105            lhs.int_val = scaled.int_val + &rhs.int_val;
106            lhs.scale = rhs.scale;
107        }
108        Ordering::Greater => {
109            let scaled = rhs.with_scale(lhs.scale);
110            lhs.int_val += scaled.int_val;
111        }
112        Ordering::Equal => {
113            lhs.int_val += &rhs.int_val;
114        }
115    }
116}
117
118/// Add BigDecimal references which have the same scale (integer addition)
119fn add_aligned_bigdecimal_ref_ref(
120    lhs: BigDecimalRef, rhs: BigDecimalRef
121) -> BigDecimal {
122    debug_assert!(!lhs.is_zero() && !rhs.is_zero());
123    debug_assert_eq!(lhs.scale, rhs.scale);
124
125    if lhs.digits.bits() >= rhs.digits.bits() {
126        lhs.to_owned() + rhs
127    } else {
128        rhs.to_owned() + lhs
129    }
130}
131
132fn add_unaligned_bigdecimal_ref_ref(
133    lhs: BigDecimalRef, rhs: BigDecimalRef, _ctx: Option<&Context>,
134) -> BigDecimal {
135    debug_assert!(!lhs.is_zero() && !rhs.is_zero());
136    debug_assert!(lhs.scale >= rhs.scale);
137
138    let scale_diff = (lhs.scale - rhs.scale) as u64;
139
140    let shifted_rhs_digits = rhs.digits * ten_to_the_uint(scale_diff);
141    let shifted_rhs_int = BigInt::from_biguint(rhs.sign, shifted_rhs_digits);
142    let shifted_rhs = BigDecimal::new(shifted_rhs_int, lhs.scale);
143
144    shifted_rhs + lhs
145}
146
147
148#[cfg(test)]
149mod test {
150    use super::*;
151
152    include!("addition.tests.rs");
153}