bigdecimal/arithmetic/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//! arithmetic routines

use crate::*;
use num_traits::CheckedSub;

pub(crate) mod addition;
pub(crate) mod sqrt;
pub(crate) mod cbrt;
pub(crate) mod inverse;

/// Return 10^pow
///
/// Try to calculate this with fewest number of allocations
///
pub(crate) fn ten_to_the(pow: u64) -> BigInt {
    ten_to_the_uint(pow).into()
}

/// Return 10^{pow} as u64
pub(crate) fn ten_to_the_u64(pow: u8) -> u64 {
    debug_assert!(pow < 20);
    10u64.pow(pow as u32)
}

/// Return 10^pow
pub(crate) fn ten_to_the_uint(pow: u64) -> BigUint {
    if pow < 20 {
        return BigUint::from(10u64.pow(pow as u32));
    }

    // linear case of 10^pow = 10^(19 * count + rem)
    if pow < 590 {
        let ten_to_nineteen = 10u64.pow(19);

        // count factors of 19
        let (count, rem) = pow.div_rem(&19);

        let mut res = BigUint::from(ten_to_nineteen);
        for _ in 1..count {
            res *= ten_to_nineteen;
        }
        if rem != 0 {
            res *= 10u64.pow(rem as u32);
        }

        return res;
    }

    // use recursive algorithm where linear case might be too slow
    let (quotient, rem) = pow.div_rem(&16);
    let x = ten_to_the_uint(quotient);

    let x2 = &x * &x;
    let x4 = &x2 * &x2;
    let x8 = &x4 * &x4;
    let res = &x8 * &x8;

    if rem == 0 {
        res
    } else {
        res * 10u64.pow(rem as u32)
    }
}

pub(crate) fn multiply_by_ten_to_the_uint<T, P>(n: &mut T, pow: P)
    where T: MulAssign<u64> + MulAssign<BigUint>,
          P: ToPrimitive
{
    let pow = pow.to_u64().expect("exponent overflow error");
    if pow < 20 {
        *n *= 10u64.pow(pow as u32);
    } else {
        *n *= ten_to_the_uint(pow);
    }

}

/// Return number of decimal digits in integer
pub(crate) fn count_decimal_digits(int: &BigInt) -> u64 {
    count_decimal_digits_uint(int.magnitude())
}

/// Return number of decimal digits in unsigned integer
pub(crate) fn count_decimal_digits_uint(uint: &BigUint) -> u64 {
    if uint.is_zero() {
        return 1;
    }
    let mut digits = (uint.bits() as f64 / LOG2_10) as u64;
    // guess number of digits based on number of bits in UInt
    let mut num = ten_to_the_uint(digits);
    while *uint >= num {
        num *= 10u8;
        digits += 1;
    }
    digits
}


/// Return difference of two numbers, returning diff as u64
pub(crate) fn diff<T>(a: T, b: T) -> (Ordering, u64)
where
    T: ToPrimitive + CheckedSub + stdlib::cmp::Ord
{
    use stdlib::cmp::Ordering::*;

    let (ord, diff) = checked_diff(a, b);

    (ord, diff.expect("subtraction overflow"))
}

/// Return difference of two numbers. If num doesn't fit in u64, return None
pub(crate) fn checked_diff<T>(a: T, b: T) -> (Ordering, Option<u64>)
where
    T: ToPrimitive + CheckedSub + stdlib::cmp::Ord
{
    use stdlib::cmp::Ordering::*;

    let _try_subtracting = |x:T, y:T| x.checked_sub(&y).and_then(|diff| diff.to_u64());

    match a.cmp(&b) {
        Less => (Less, _try_subtracting(b, a)),
        Greater => (Greater, _try_subtracting(a, b)),
        Equal => (Equal, Some(0)),
    }
}

/// Return difference of two numbers, returning diff as usize
#[allow(dead_code)]
pub(crate) fn diff_usize(a: usize, b: usize) -> (Ordering, usize) {
    use stdlib::cmp::Ordering::*;

    match a.cmp(&b) {
        Less => (Less, b - a),
        Greater => (Greater, a - b),
        Equal => (Equal, 0),
    }
}


/// Add carry to given number, returning trimmed value and storing overflow back in carry
///
pub(crate) fn add_carry(n: u8, carry: &mut u8) -> u8 {
    let s = n + *carry;
    if s < 10 {
        *carry = 0;
        s
    } else {
        debug_assert!(s < 20);
        *carry = 1;
        s - 10
    }
}


/// If n is greater than 10, split and store overflow in carry
///
/// No action if n is less than 10.
///
/// Carry is not allowed to be 1 if n is two digits
///
pub(crate) fn store_carry(n: u8, carry: &mut u8) -> u8 {
    if n < 10 {
        n
    } else {
        debug_assert!(n < 20);
        debug_assert_eq!(carry, &0);
        *carry = 1;
        n - 10
    }
}


/// Extend destination vector with values in D, adding carry while carry is not zero
///
/// If carry overflows, it is NOT pushed into the destination vector.
///
pub(crate) fn extend_adding_with_carry<D: Iterator<Item=u8>>(
    dest: &mut Vec<u8>,
    mut digits: D,
    carry: &mut u8,
) {
    while *carry != 0 {
        match digits.next() {
            Some(d) => {
                dest.push(add_carry(d, carry))
            }
            None => {
                return;
            }
        }
    }
    dest.extend(digits);
}