Skip to main content

bigdecimal/arithmetic/
mod.rs

1//! arithmetic routines
2
3use crate::*;
4use num_traits::CheckedSub;
5use num_traits::AsPrimitive;
6
7pub(crate) mod decimal;
8
9pub(crate) mod addition;
10pub(crate) mod division;
11pub(crate) mod multiplication;
12pub(crate) mod modulo;
13pub(crate) mod sqrt;
14pub(crate) mod cbrt;
15pub(crate) mod inverse;
16pub(crate) mod pow;
17pub(crate) mod exp;
18
19pub(crate) use self::decimal::{
20    count_digits_bigint as count_decimal_digits,
21    count_digits_biguint as count_decimal_digits_uint,
22};
23
24#[cfg(not(feature = "std"))]
25mod funcs {
26    // f64::exp2 is only available in std, we have to use an external crate like libm
27    pub fn exp2(x: f64) -> f64 {
28        libm::exp2(x)
29    }
30
31    // f64::log10 is only available in std, we have to use an external crate like libm
32    pub fn log10(x: f64) -> f64 {
33        libm::log10(x)
34    }
35}
36
37#[cfg(feature = "std")]
38mod funcs {
39    pub fn exp2(x: f64) -> f64 {
40        x.exp2()
41    }
42
43    pub fn log10(x: f64) -> f64 {
44        x.log10()
45    }
46}
47
48// rexport all funcs into this module
49pub(crate) use self::funcs::*;
50
51/// Return 10^pow
52///
53/// Try to calculate this with fewest number of allocations
54///
55pub(crate) fn ten_to_the(pow: u64) -> BigInt {
56    ten_to_the_uint(pow).into()
57}
58
59/// Return 10^{pow} as u64
60pub(crate) fn ten_to_the_u64(pow: u8) -> u64 {
61    if true {
    if !(pow < 20) { ::core::panicking::panic("assertion failed: pow < 20") };
};debug_assert!(pow < 20);
62    10u64.pow(pow as u32)
63}
64
65/// Return 10^{pow} as output
66pub(crate) fn ten_to_the_t<T>(pow: u8) -> T
67where
68    T: From<u8> + num_traits::Pow<u8, Output = T>
69{
70    if true {
    if !((pow as f64) < stdlib::mem::size_of::<T>() as f64 * 8.0 / LOG2_10) {
        ::core::panicking::panic("assertion failed: (pow as f64) < stdlib::mem::size_of::<T>() as f64 * 8.0 / LOG2_10")
    };
};debug_assert!((pow as f64) < stdlib::mem::size_of::<T>() as f64 * 8.0 / LOG2_10);
71    T::from(10u8).pow(pow)
72}
73
74/// Return 10^pow
75pub(crate) fn ten_to_the_uint(pow: u64) -> BigUint {
76    if pow < 20 {
77        return BigUint::from(10u64.pow(pow as u32));
78    }
79
80    // linear case of 10^pow = 10^(19 * count + rem)
81    if pow < 590 {
82        let ten_to_nineteen = 10u64.pow(19);
83
84        // count factors of 19
85        let (count, rem) = pow.div_rem(&19);
86
87        let mut res = BigUint::from(ten_to_nineteen);
88        for _ in 1..count {
89            res *= ten_to_nineteen;
90        }
91        if rem != 0 {
92            res *= 10u64.pow(rem as u32);
93        }
94
95        return res;
96    }
97
98    // use recursive algorithm where linear case might be too slow
99    let (quotient, rem) = pow.div_rem(&16);
100    let x = ten_to_the_uint(quotient);
101
102    let x2 = &x * &x;
103    let x4 = &x2 * &x2;
104    let x8 = &x4 * &x4;
105    let res = &x8 * &x8;
106
107    if rem == 0 {
108        res
109    } else {
110        res * 10u64.pow(rem as u32)
111    }
112}
113
114pub(crate) fn multiply_by_ten_to_the_uint<T, P>(n: &mut T, pow: P)
115where
116    T: MulAssign<u64> + MulAssign<BigUint>,
117    P: ToPrimitive,
118{
119    let pow = pow.to_u64().expect("exponent overflow error");
120    if pow < 20 {
121        *n *= 10u64.pow(pow as u32);
122    } else {
123        *n *= ten_to_the_uint(pow);
124    }
125}
126
127/// Return difference of two numbers, returning diff as u64
128pub(crate) fn diff<T>(a: T, b: T) -> (Ordering, u64)
129where
130    T: ToPrimitive + CheckedSub + stdlib::cmp::Ord,
131{
132    use stdlib::cmp::Ordering::*;
133
134    let (ord, diff) = checked_diff(a, b);
135
136    (ord, diff.expect("subtraction overflow"))
137}
138
139/// Return difference of two numbers. If num doesn't fit in u64, return None
140pub(crate) fn checked_diff<T>(a: T, b: T) -> (Ordering, Option<u64>)
141where
142    T: ToPrimitive + CheckedSub + stdlib::cmp::Ord,
143{
144    use stdlib::cmp::Ordering::*;
145
146    let _try_subtracting = |x: T, y: T| x.checked_sub(&y).and_then(|diff| diff.to_u64());
147
148    match a.cmp(&b) {
149        Less => (Less, _try_subtracting(b, a)),
150        Greater => (Greater, _try_subtracting(a, b)),
151        Equal => (Equal, Some(0)),
152    }
153}
154
155/// Return difference of two numbers, returning diff as usize
156#[allow(dead_code)]
157pub(crate) fn diff_usize<T>(a: T, b: T) -> (Ordering, usize)
158where
159    T: AsPrimitive<usize> + stdlib::ops::Sub<Output = T> + stdlib::cmp::Ord,
160{
161    use stdlib::cmp::Ordering::*;
162
163    match a.cmp(&b) {
164        Less => (Less, (b - a).as_()),
165        Greater => (Greater, (a - b).as_()),
166        Equal => (Equal, 0),
167    }
168}
169
170/// Return absolute difference between two numbers
171#[cfg(rustc_1_60)]
172#[allow(clippy::incompatible_msrv)]
173#[allow(dead_code)]
174pub(crate) fn abs_diff(x: i64, y: i64) -> u64 {
175    x.abs_diff(y)
176}
177
178#[cfg(not(rustc_1_60))]
179#[allow(dead_code)]
180pub(crate) fn abs_diff(x: i64, y: i64) -> u64 {
181    (x as i128 - y as i128).to_u64().unwrap_or(0)
182}
183
184/// Add carry to given number, returning trimmed value and storing overflow back in carry
185///
186pub(crate) fn add_carry(n: u8, carry: &mut u8) -> u8 {
187    let s = n + *carry;
188    if s < 10 {
189        *carry = 0;
190        s
191    } else {
192        if true {
    if !(s < 20) { ::core::panicking::panic("assertion failed: s < 20") };
};debug_assert!(s < 20);
193        *carry = 1;
194        s - 10
195    }
196}
197
198/// If n is greater than 10, split and store overflow in carry
199///
200/// No action if n is less than 10.
201///
202/// Carry is not allowed to be 1 if n is two digits
203///
204pub(crate) fn store_carry(n: u8, carry: &mut u8) -> u8 {
205    if n < 10 {
206        n
207    } else {
208        if true {
    if !(n < 20) { ::core::panicking::panic("assertion failed: n < 20") };
};debug_assert!(n < 20);
209        if true {
    {
        match (&carry, &&0) {
            (left_val, right_val) => {
                if !(*left_val == *right_val) {
                    let kind = ::core::panicking::AssertKind::Eq;
                    ::core::panicking::assert_failed(kind, &*left_val,
                        &*right_val, ::core::option::Option::None);
                }
            }
        }
    };
};debug_assert_eq!(carry, &0);
210        *carry = 1;
211        n - 10
212    }
213}
214
215/// Extend destination vector with values in D, adding carry while carry is not zero
216///
217/// If carry overflows, it is NOT pushed into the destination vector.
218///
219pub(crate) fn extend_adding_with_carry<D: Iterator<Item = u8>>(
220    dest: &mut Vec<u8>,
221    mut digits: D,
222    carry: &mut u8,
223) {
224    while *carry != 0 {
225        match digits.next() {
226            Some(d) => {
227                dest.push(add_carry(d, carry))
228            }
229            None => {
230                return;
231            }
232        }
233    }
234    dest.extend(digits);
235}