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