bnum/bint/
checked.rs

1macro_rules! checked_ilog {
2    ($method: ident $(, $base: ident: $ty: ty)?) => {
3        #[doc = doc::checked::$method!(I)]
4        #[must_use = doc::must_use_op!()]
5        #[inline]
6        pub const fn $method(self $(, $base: $ty)?) -> Option<ExpType> {
7            if self.is_negative() {
8                None
9            } else {
10                self.bits.$method($($base)?)
11            }
12        }
13    }
14}
15
16use crate::doc;
17use crate::helpers::tuple_to_option;
18use crate::ExpType;
19
20macro_rules! checked {
21    ($BUint: ident, $BInt: ident, $Digit: ident) => {
22        #[doc = doc::checked::impl_desc!()]
23        impl<const N: usize> $BInt<N> {
24            #[doc = doc::checked::checked_add!(I)]
25            #[must_use = doc::must_use_op!()]
26            #[inline]
27            pub const fn checked_add(self, rhs: Self) -> Option<Self> {
28                tuple_to_option(self.overflowing_add(rhs))
29            }
30
31            #[doc = doc::checked::checked_add_unsigned!(I)]
32            #[must_use = doc::must_use_op!()]
33            #[inline]
34            pub const fn checked_add_unsigned(self, rhs: $BUint<N>) -> Option<Self> {
35                tuple_to_option(self.overflowing_add_unsigned(rhs))
36            }
37
38            #[doc = doc::checked::checked_sub!(I)]
39            #[must_use = doc::must_use_op!()]
40            #[inline]
41            pub const fn checked_sub(self, rhs: Self) -> Option<Self> {
42                tuple_to_option(self.overflowing_sub(rhs))
43            }
44
45            #[doc = doc::checked::checked_sub_unsigned!(I)]
46            #[must_use = doc::must_use_op!()]
47            #[inline]
48            pub const fn checked_sub_unsigned(self, rhs: $BUint<N>) -> Option<Self> {
49                tuple_to_option(self.overflowing_sub_unsigned(rhs))
50            }
51
52            #[doc = doc::checked::checked_mul!(I)]
53            #[must_use = doc::must_use_op!()]
54            #[inline]
55            pub const fn checked_mul(self, rhs: Self) -> Option<Self> {
56                tuple_to_option(self.overflowing_mul(rhs))
57            }
58
59            #[doc = doc::checked::checked_div!(I)]
60            #[must_use = doc::must_use_op!()]
61            #[inline]
62            pub const fn checked_div(self, rhs: Self) -> Option<Self> {
63                if rhs.is_zero() {
64                    None
65                } else {
66                    tuple_to_option(self.overflowing_div(rhs))
67                }
68            }
69
70            #[doc = doc::checked::checked_div_euclid!(I)]
71            #[must_use = doc::must_use_op!()]
72            #[inline]
73            pub const fn checked_div_euclid(self, rhs: Self) -> Option<Self> {
74                if rhs.is_zero() {
75                    None
76                } else {
77                    tuple_to_option(self.overflowing_div_euclid(rhs))
78                }
79            }
80
81            #[doc = doc::checked::checked_rem!(I)]
82            #[must_use = doc::must_use_op!()]
83            #[inline]
84            pub const fn checked_rem(self, rhs: Self) -> Option<Self> {
85                if rhs.is_zero() {
86                    None
87                } else {
88                    tuple_to_option(self.overflowing_rem(rhs))
89                }
90            }
91
92            #[doc = doc::checked::checked_rem_euclid!(I)]
93            #[must_use = doc::must_use_op!()]
94            #[inline]
95            pub const fn checked_rem_euclid(self, rhs: Self) -> Option<Self> {
96                if rhs.is_zero() {
97                    None
98                } else {
99                    tuple_to_option(self.overflowing_rem_euclid(rhs))
100                }
101            }
102
103            #[doc = doc::checked::checked_neg!(I)]
104            #[must_use = doc::must_use_op!()]
105            #[inline]
106            pub const fn checked_neg(self) -> Option<Self> {
107                tuple_to_option(self.overflowing_neg())
108            }
109
110            #[doc = doc::checked::checked_shl!(I)]
111            #[must_use = doc::must_use_op!()]
112            #[inline]
113            pub const fn checked_shl(self, rhs: ExpType) -> Option<Self> {
114                tuple_to_option(self.overflowing_shl(rhs))
115            }
116
117            #[doc = doc::checked::checked_shr!(I)]
118            #[must_use = doc::must_use_op!()]
119            #[inline]
120            pub const fn checked_shr(self, rhs: ExpType) -> Option<Self> {
121                tuple_to_option(self.overflowing_shr(rhs))
122            }
123
124            #[doc = doc::checked::checked_abs!(I)]
125            #[must_use = doc::must_use_op!()]
126            #[inline]
127            pub const fn checked_abs(self) -> Option<Self> {
128                tuple_to_option(self.overflowing_abs())
129            }
130
131            #[doc = doc::checked::checked_pow!(I)]
132            #[must_use = doc::must_use_op!()]
133            #[inline]
134            pub const fn checked_pow(self, pow: ExpType) -> Option<Self> {
135                match self.unsigned_abs().checked_pow(pow) {
136                    Some(u) => {
137                        let out = Self::from_bits(u);
138                        let neg = self.is_negative();
139                        if !neg || pow & 1 == 0 {
140                            if out.is_negative() {
141                                None
142                            } else {
143                                Some(out)
144                            }
145                        } else {
146                            let out = out.wrapping_neg();
147                            if !out.is_negative() {
148                                None
149                            } else {
150                                Some(out)
151                            }
152                        }
153                    }
154                    None => None,
155                }
156            }
157
158            #[doc = doc::checked::checked_next_multiple_of!(I)]
159            #[must_use = doc::must_use_op!()]
160            #[inline]
161            pub const fn checked_next_multiple_of(self, rhs: Self) -> Option<Self> {
162                if rhs.is_zero() {
163                    return None;
164                }
165                let rem = self.wrapping_rem_euclid(rhs);
166                if rem.is_zero() {
167                    return Some(self);
168                }
169                if rem.is_negative() == rhs.is_negative() {
170                    self.checked_add(rhs.wrapping_sub(rem))
171                } else {
172                    self.checked_sub(rem)
173                }
174            }
175
176            #[doc = doc::checked::checked_ilog!(I)]
177            #[must_use = doc::must_use_op!()]
178            #[inline]
179            pub const fn checked_ilog(self, base: Self) -> Option<ExpType> {
180                if base.is_negative() || self.is_negative() {
181                    None
182                } else {
183                    self.to_bits().checked_ilog(base.to_bits())
184                }
185            }
186
187            checked_ilog!(checked_ilog2);
188            checked_ilog!(checked_ilog10);
189        }
190    };
191}
192
193
194#[cfg(test)]
195crate::test::all_digit_tests! {
196    use crate::test::test_bignum;
197    use crate::test::types::{itest, utest};
198
199    test_bignum! {
200        function: <itest>::checked_add(a: itest, b: itest),
201        cases: [
202            (itest::MAX, -1i8)
203        ]
204    }
205    test_bignum! {
206        function: <itest>::checked_add_unsigned(a: itest, b: utest)
207    }
208    test_bignum! {
209        function: <itest>::checked_sub(a: itest, b: itest),
210        cases: [
211            (itest::MIN, -1i8)
212        ]
213    }
214    test_bignum! {
215        function: <itest>::checked_sub_unsigned(a: itest, b: utest)
216    }
217    test_bignum! {
218        function: <itest>::checked_mul(a: itest, b: itest),
219        cases: [
220            (itest::MIN, -1i8)
221        ]
222    }
223    test_bignum! {
224        function: <itest>::checked_div(a: itest, b: itest),
225        cases: [
226            (0i8, 0i8),
227            (23098403i32 as itest, 0i8),
228            (itest::MIN, -1i8),
229            (8388600i32 as itest, 68201i32 as itest) // tests the unlikely condition in the division algorithm at step D5
230        ]
231    }
232    test_bignum! {
233        function: <itest>::checked_div_euclid(a: itest, b: itest),
234        cases: [
235            (itest::MIN, -1i8),
236            (0i8, 0i8)
237        ]
238    }
239    test_bignum! {
240        function: <itest>::checked_rem(a: itest, b: itest),
241        cases: [
242            (itest::MIN, -1i8),
243            (0i8, 0i8)
244        ]
245    }
246    test_bignum! {
247        function: <itest>::checked_rem_euclid(a: itest, b: itest),
248        skip: b <= u8::MAX as itest,
249        cases: [
250            (itest::MIN, -1i8),
251            (0i8, 0i8)
252        ]
253    }
254    test_bignum! {
255        function: <itest>::checked_neg(a: itest),
256        cases: [
257            (itest::MIN)
258        ]
259    }
260    test_bignum! {
261        function: <itest>::checked_shl(a: itest, b: u16)
262    }
263    test_bignum! {
264        function: <itest>::checked_shr(a: itest, b: u16)
265    }
266    test_bignum! {
267        function: <itest>::checked_pow(a: itest, b: u16),
268        cases: [
269            (2i8, itest::BITS as u16 - 1),
270            (-2i8, itest::BITS as u16 - 1)
271        ]
272    }
273    test_bignum! {
274        function: <itest>::checked_ilog2(a: itest)
275    }
276    test_bignum! {
277        function: <itest>::checked_ilog10(a: itest)
278    }
279    test_bignum! {
280        function: <itest>::checked_ilog(a: itest, b: itest)
281    }
282}
283
284crate::macro_impl!(checked);