Skip to main content

bnum/buint/
checked.rs

1use crate::digit;
2use crate::doc;
3use crate::errors::div_zero;
4use crate::helpers::tuple_to_option;
5use crate::ExpType;
6
7macro_rules! checked {
8    ($BUint: ident, $BInt: ident, $Digit: ident) => {
9        #[doc = doc::checked::impl_desc!()]
10        impl<const N: usize> $BUint<N> {
11            #[doc = doc::checked::checked_add!(U)]
12            #[must_use = doc::must_use_op!()]
13            #[inline]
14            pub const fn checked_add(self, rhs: Self) -> Option<Self> {
15                tuple_to_option(self.overflowing_add(rhs))
16            }
17
18            #[doc = doc::checked::checked_add_signed!(U)]
19            #[must_use = doc::must_use_op!()]
20            #[inline]
21            pub const fn checked_add_signed(self, rhs: $BInt<N>) -> Option<Self> {
22                tuple_to_option(self.overflowing_add_signed(rhs))
23            }
24
25            #[doc = doc::checked::checked_sub!(U)]
26            #[must_use = doc::must_use_op!()]
27            #[inline]
28            pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
29                tuple_to_option(self.overflowing_sub(rhs))
30            }
31
32            #[doc = doc::checked::checked_mul!(U)]
33            #[must_use = doc::must_use_op!()]
34            #[inline]
35            pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
36                tuple_to_option(self.overflowing_mul(rhs))
37            }
38
39            pub(crate) const fn div_rem_digit(self, rhs: $Digit) -> (Self, $Digit) {
40                let mut out = Self::ZERO;
41                let mut rem: $Digit = 0;
42                let mut i = N;
43                while i > 0 {
44                    i -= 1;
45                    let (q, r) = digit::$Digit::div_rem_wide(self.digits[i], rem, rhs);
46                    rem = r;
47                    out.digits[i] = q;
48                }
49                (out, rem)
50            }
51            
52            
53            #[inline]
54            pub(crate) const fn div_rem_unchecked(self, rhs: Self) -> (Self, Self) {
55                use core::cmp::Ordering;
56                
57                if self.is_zero() {
58                    return (Self::ZERO, Self::ZERO);
59                }
60                
61                match self.cmp(&rhs) {
62                    Ordering::Less => (Self::ZERO, self),
63                    Ordering::Equal => (Self::ONE, Self::ZERO),
64                    Ordering::Greater => {
65                        let ldi = rhs.last_digit_index();
66                        if ldi == 0 {
67                            let (div, rem) = self.div_rem_digit(rhs.digits[0]);
68                            (div, Self::from_digit(rem))
69                        } else {
70                            self.basecase_div_rem(rhs, ldi + 1)
71                        }
72                    }
73                }
74            }
75            
76            #[inline]
77            pub(crate) const fn div_rem(self, rhs: Self) -> (Self, Self) {
78                if rhs.is_zero() {
79                    div_zero!()
80                } else {
81                    self.div_rem_unchecked(rhs)
82                }
83            }
84            
85            #[doc = doc::checked::checked_div!(U)]
86            #[must_use = doc::must_use_op!()]
87            #[inline]
88            pub const fn checked_div(self, rhs: Self) -> Option<Self> {
89                if rhs.is_zero() {
90                    None
91                } else {
92                    Some(self.div_rem_unchecked(rhs).0)
93                }
94            }
95            
96            #[doc = doc::checked::checked_div_euclid!(U)]
97            #[must_use = doc::must_use_op!()]
98            #[inline]
99            pub const fn checked_div_euclid(self, rhs: Self) -> Option<Self> {
100                self.checked_div(rhs)
101            }
102            
103            #[doc = doc::checked::checked_rem!(U)]
104            #[must_use = doc::must_use_op!()]
105            #[inline]
106            pub const fn checked_rem(self, rhs: Self) -> Option<Self> {
107                if rhs.is_zero() {
108                    None
109                } else {
110                    Some(self.div_rem_unchecked(rhs).1)
111                }
112            }
113            
114            #[doc = doc::checked::checked_rem_euclid!(U)]
115            #[must_use = doc::must_use_op!()]
116            #[inline]
117            pub const fn checked_rem_euclid(self, rhs: Self) -> Option<Self> {
118                self.checked_rem(rhs)
119            }
120        
121            #[doc = doc::checked::checked_neg!(U)]
122            #[must_use = doc::must_use_op!()]
123            #[inline]
124            pub const fn checked_neg(self) -> Option<Self> {
125                if self.is_zero() {
126                    Some(self)
127                } else {
128                    None
129                }
130            }
131
132            #[doc = doc::checked::checked_shl!(U)]
133            #[must_use = doc::must_use_op!()]
134            #[inline]
135            pub const fn checked_shl(self, rhs: ExpType) -> Option<Self> {
136                if rhs >= Self::BITS {
137                    None
138                } else {
139                    unsafe {
140                        Some(Self::unchecked_shl_internal(self, rhs))
141                    }
142                }
143            }
144
145            #[doc = doc::checked::checked_shr!(U)]
146            #[must_use = doc::must_use_op!()]
147            #[inline]
148            pub const fn checked_shr(self, rhs: ExpType) -> Option<Self> {
149                if rhs >= Self::BITS {
150                    None
151                } else {
152                    unsafe {
153                        Some(Self::unchecked_shr_internal(self, rhs))
154                    }
155                }
156            }
157
158            #[doc = doc::checked::checked_pow!(U)]
159            #[must_use = doc::must_use_op!()]
160            #[inline]
161            pub const fn checked_pow(mut self, mut pow: ExpType) -> Option<Self> {
162                // https://en.wikipedia.org/wiki/Exponentiation_by_squaring#Basic_method
163                if pow == 0 {
164                    return Some(Self::ONE);
165                }
166                let mut y = Self::ONE;
167                while pow > 1 {
168                    if pow & 1 == 1 {
169                        y = match self.checked_mul(y) {
170                            Some(m) => m,
171                            None => return None,
172                        };
173                    }
174                    self = match self.checked_mul(self) {
175                        Some(m) => m,
176                        None => return None,
177                    };
178                    pow >>= 1;
179                }
180                self.checked_mul(y)
181            }
182
183            #[doc = doc::checked::checked_next_multiple_of!(U)]
184            #[must_use = doc::must_use_op!()]
185            #[inline]
186            pub const fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> {
187                match self.checked_rem(rhs) {
188                    Some(rem) => {
189                        if rem.is_zero() {
190                            // `rhs` divides `self` exactly so just return `self`
191                            Some(self)
192                        } else {
193                            // `next_multiple = floor(self / rhs) * rhs + rhs = (self - rem) + rhs`
194                            self.checked_add(rhs.sub(rem))
195                        }
196                    },
197                    None => None,
198                }
199            }
200
201            #[doc = doc::checked::checked_ilog2!(U)]
202            #[must_use = doc::must_use_op!()]
203            #[inline]
204            pub const fn checked_ilog2(self) -> Option<ExpType> {
205                self.bits().checked_sub(1)
206            }
207
208            #[inline]
209            const fn iilog(m: ExpType, b: Self, k: Self) -> (ExpType, Self) {
210                // https://people.csail.mit.edu/jaffer/III/iilog.pdf
211                if b.gt(&k) {
212                    (m, k)
213                } else {
214                    let (new, q) = Self::iilog(m << 1, b.mul(b), k.div_rem_unchecked(b).0);
215                    if b.gt(&q) {
216                        (new, q)
217                    } else {
218                        (new + m, q.div(b))
219                    }
220                }
221            }
222
223            #[doc = doc::checked::checked_ilog10!(U)]
224            #[must_use = doc::must_use_op!()]
225            #[inline]
226            pub const fn checked_ilog10(self) -> Option<ExpType> {
227                if self.is_zero() {
228                    return None;
229                }
230                if Self::TEN.gt(&self) {
231                    return Some(0);
232                }
233                Some(Self::iilog(1, Self::TEN, self.div_rem_digit(10).0).0)
234            }
235
236            #[doc = doc::checked::checked_ilog!(U)]
237            #[must_use = doc::must_use_op!()]
238            #[inline]
239            pub const fn checked_ilog(self, base: Self) -> Option<ExpType> {
240                use core::cmp::Ordering;
241                match base.cmp(&Self::TWO) {
242                    Ordering::Less => None,
243                    Ordering::Equal => self.checked_ilog2(),
244                    Ordering::Greater => {
245                        if self.is_zero() {
246                            return None;
247                        }
248                        if base.gt(&self) {
249                            return Some(0);
250                        }
251                        Some(Self::iilog(1, base, self.div(base)).0)
252                    }
253                }
254            }
255
256            #[doc = doc::checked::checked_next_power_of_two!(U 256)]
257            #[must_use = doc::must_use_op!()]
258            #[inline]
259            pub const fn checked_next_power_of_two(self) -> Option<Self> {
260                if self.is_power_of_two() {
261                    return Some(self);
262                }
263                let bits = self.bits();
264                if bits == Self::BITS {
265                    return None;
266                }
267                Some(Self::power_of_two(bits))
268            }
269        }
270    };
271}
272
273#[cfg(test)]
274crate::test::all_digit_tests! {
275    use crate::test::{test_bignum, types::*};
276
277    test_bignum! {
278        function: <utest>::checked_add(a: utest, b: utest),
279        cases: [
280            (utest::MAX, 1u8)
281        ]
282    }
283    test_bignum! {
284        function: <utest>::checked_add_signed(a: utest, b: itest)
285    }
286    test_bignum! {
287        function: <utest>::checked_sub(a: utest, b: utest)
288    }
289    test_bignum! {
290        function: <utest>::checked_mul(a: utest, b: utest)
291    }
292    test_bignum! {
293        function: <utest>::checked_div(a: utest, b: utest),
294        cases: [
295            (328622u32 as utest, 10000u32 as utest), // tests the unlikely condition in the division algorithm at step D5
296            (2074086u32 as utest, 76819u32 as utest) // tests the unlikely condition in the division algorithm at step D5
297        ]
298    }
299    test_bignum! {
300        function: <utest>::checked_div_euclid(a: utest, b: utest)
301    }
302    test_bignum! {
303        function: <utest>::checked_rem(a: utest, b: utest)
304    }
305    test_bignum! {
306        function: <utest>::checked_rem_euclid(a: utest, b: utest)
307    }
308    test_bignum! {
309        function: <utest>::checked_neg(a: utest)
310    }
311    test_bignum! {
312        function: <utest>::checked_shl(a: utest, b: u16)
313    }
314    test_bignum! {
315        function: <utest>::checked_shr(a: utest, b: u16)
316    }
317    test_bignum! {
318        function: <utest>::checked_pow(a: utest, b: u16)
319    }
320    test_bignum! {
321        function: <utest>::checked_ilog(a: utest, b: utest),
322        cases: [
323            (2u8, 60u8),
324            (utest::MAX, 2u8)
325        ]
326    }
327    test_bignum! {
328        function: <utest>::checked_ilog2(a: utest)
329    }
330    test_bignum! {
331        function: <utest>::checked_ilog10(a: utest)
332    }
333    test_bignum! {
334        function: <utest>::checked_next_power_of_two(a: utest),
335        cases: [
336            (utest::MAX)
337        ]
338    }
339}
340
341crate::macro_impl!(checked);