devela/num/
_private.rs

1// devela::num::_private
2//
3//! private numeric helpers
4//
5// TOC
6// - upcasted_op
7// - impl_ops
8
9#![allow(unused, non_camel_case_types)]
10
11/// helper macro to only do checked operations when we can't upcast (i.e. for 128-bits).
12///
13/// Performs checked operations only if the upcasted type is the same
14/// as the non-upcasted one. It is compatible with const functions.
15///
16/// # Arguments
17/// `$lhs`:      the left hand side operator
18/// `$rhs`:      the right hand side operator
19/// `$modulus`:  the modulus (for reduced_* ops)
20/// `$ba`:       the base type
21/// `$up`:       the upcasted type
22///
23/// # Invoked from:
24/// - num/int/wrapper/impl_root.rs
25///
26/// # Examples
27/// ```ignore
28/// let sum = upcasted_op![add_err(v, m) i32 => i64];
29/// let sum = upcasted_op![reduced_add_err(v, m) % 40; i32 => i64];
30/// ```
31/// # Features
32/// It makes use of `unsafe_hint` to optimize arithmetic ops when able to upcast.
33//
34// TODO IMPROVE: try to unify with impl_modulo::upcastop
35macro_rules! upcasted_op {
36    (
37    /* basic arithmetic ops */
38    // if we've not upcasted, do checked operation and return err on overflow
39    add_err($lhs:expr, $rhs:expr) $ba:ty => $up:ty) => {
40        if $crate::cif!(diff($ba, $up)) {
41            #[cfg(any(feature = "safe_num", not(feature = "unsafe_hint")))]
42            {
43                $lhs + $rhs
44            }
45            #[cfg(all(not(feature = "safe_num"), feature = "unsafe_hint"))]
46            // SAFETY: can't overflow if upcasted
47            unsafe {
48                $lhs.unchecked_add($rhs)
49            }
50        } else {
51            if let Some(sum) = $lhs.checked_add($rhs) {
52                sum
53            } else {
54                return Err($crate::NumError::Overflow(None));
55            }
56        }
57    };
58    (mul_err($lhs:expr, $rhs:expr) $ba:ty => $up:ty) => {
59        if $crate::cif!(diff($ba, $up)) {
60            #[cfg(any(feature = "safe_num", not(feature = "unsafe_hint")))]
61            {
62                $lhs * $rhs
63            }
64            #[cfg(all(not(feature = "safe_num"), feature = "unsafe_hint"))]
65            // SAFETY: can't overflow if upcasted
66            unsafe {
67                $lhs.unchecked_mul($rhs)
68            }
69        } else {
70            if let Some(product) = $lhs.checked_mul($rhs) {
71                product
72            } else {
73                return Err($crate::NumError::Overflow(None));
74            }
75        }
76    };
77    (
78    /* reduced (modulo) ops */
79
80    // if we've not upcasted, first reduce the sumands with the given modulus,
81    // then do checked operation and return err on overflow
82    reduced_add_err($lhs:expr, $rhs:expr) % $modulus:expr; $ba:ty => $up:ty) => {
83        if $crate::cif!(diff($ba, $up)) {
84            #[cfg(any(feature = "safe_num", not(feature = "unsafe_hint")))]
85            {
86                $lhs + $rhs
87            }
88            #[cfg(all(not(feature = "safe_num"), feature = "unsafe_hint"))]
89            // SAFETY: can't overflow if upcasted
90            unsafe {
91                $lhs.unchecked_add($rhs)
92            }
93        } else {
94            // reduce each sumand before checked operation
95            if let Some(sum) = ($lhs % $modulus).checked_add($rhs % $modulus) {
96                sum
97            } else {
98                return Err($crate::NumError::Overflow(None));
99            }
100        }
101    };
102    (
103    // if we've not upcasted, just reduce the sumands with the given $modulus
104    reduced_add($lhs:expr, $rhs:expr) % $modulus:expr; $ba:ty => $up:ty) => {
105        if $crate::cif!(diff($ba, $up)) {
106            #[cfg(any(feature = "safe_num", not(feature = "unsafe_hint")))]
107            {
108                $lhs + $rhs
109            }
110            #[cfg(all(not(feature = "safe_num"), feature = "unsafe_hint"))]
111            // SAFETY: can't overflow if upcasted
112            unsafe {
113                $lhs.unchecked_add($rhs)
114            }
115        } else {
116            // reduce each operand before the operation that could panic
117            ($lhs % $modulus) + ($rhs % $modulus)
118        }
119    };
120    (reduced_mul_err($lhs:expr, $rhs:expr) % $modulus:expr; $ba:ty => $up:ty) => {
121        if $crate::cif!(diff($ba, $up)) {
122            #[cfg(any(feature = "safe_num", not(feature = "unsafe_hint")))]
123            {
124                $lhs * $rhs
125            }
126            #[cfg(all(not(feature = "safe_num"), feature = "unsafe_hint"))]
127            // SAFETY: can't overflow if upcasted
128            unsafe {
129                $lhs.unchecked_mul($rhs)
130            }
131        } else {
132            // reduce each factor before checked operation
133            if let Some(product) = ($lhs % $modulus).checked_mul($rhs % $modulus) {
134                product
135            } else {
136                return Err($crate::NumError::Overflow(None));
137            }
138        }
139    };
140    (reduced_mul($lhs:expr, $rhs:expr) % $modulus:expr; $ba:ty => $up:ty) => {
141        if $crate::cif!(diff($ba, $up)) {
142            #[cfg(any(feature = "safe_num", not(feature = "unsafe_hint")))]
143            {
144                $lhs * $rhs
145            }
146            #[cfg(all(not(feature = "safe_num"), feature = "unsafe_hint"))]
147            // SAFETY: can't overflow if upcasted
148            unsafe {
149                $lhs.unchecked_mul($rhs)
150            }
151        } else {
152            // reduce each operand before the operation that could panic
153            ($lhs % $modulus) + ($rhs % $modulus)
154        }
155    };
156}
157pub(crate) use upcasted_op;
158
159/// implement the arithmetic operators for a unit struct wrapper, based on the inner type
160///
161/// # Arguments:
162/// $W:   the outer wrapper
163/// $T:   the inner type
164/// $cap: the capability feature that enables the given implementation. E.g "_int_i8".
165///
166/// # Invoked from:
167/// - num/int/wrapper/mod.rs
168/// - num/float/wrapper/mod.rs
169macro_rules! impl_ops {
170    ($W:ident: $($T:ty : $cap:literal ),+) => { $(
171        $crate::num::impl_ops![@common $W($T:$cap)];
172        $crate::num::impl_ops![@neg $W($T:$cap)];
173    )+ };
174    ($W:ident: (no_neg) $($T:ty : $cap:literal),+) => { $(
175        $crate::num::impl_ops![@common $W($T:$cap)];
176    )+ };
177
178    (@common $W:ident($T:ty : $cap:literal)) => {
179        $crate::num::impl_ops![@op $W($T:$cap), Add, add];
180        $crate::num::impl_ops![@op $W($T:$cap), Sub, sub];
181        $crate::num::impl_ops![@op $W($T:$cap), Mul, mul];
182        $crate::num::impl_ops![@op $W($T:$cap), Div, div];
183        $crate::num::impl_ops![@op $W($T:$cap), Rem, rem];
184        $crate::num::impl_ops![@op_assign $W($T:$cap), AddAssign, add_assign];
185        $crate::num::impl_ops![@op_assign $W($T:$cap), SubAssign, sub_assign];
186        $crate::num::impl_ops![@op_assign $W($T:$cap), MulAssign, mul_assign];
187        $crate::num::impl_ops![@op_assign $W($T:$cap), DivAssign, div_assign];
188        $crate::num::impl_ops![@op_assign $W($T:$cap), RemAssign, rem_assign];
189    };
190    (@neg $W:ident($T:ty : $cap:literal)) => {
191        #[cfg(feature = $cap )]
192        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
193        impl core::ops::Neg for $W<$T> {
194            type Output = $W<$T>;
195            fn neg(self) -> $W<$T> { $W(self.0.neg()) }
196        }
197    };
198
199    (
200    // $wrap:  the wrapper type
201    // $T:     the inner type
202    // $trait: the trait to implement
203    // $fn:    the name of the method
204        @op $W:ident($T:ty : $cap:literal), $trait:ident, $fn:ident) => {
205        /* $W<$T> op $W<$T> -> $W<$T> */
206        #[cfg(feature = $cap )]
207        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
208        impl core::ops::$trait for $W<$T> {
209            $crate::num::impl_ops![@op_body $W($T), $fn, $W<$T>, 0];
210        }
211        #[cfg(feature = $cap )]
212        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
213        impl<'s> core::ops::$trait<$W<$T>> for &'s $W<$T> {
214            $crate::num::impl_ops![@op_body $W($T), $fn, $W<$T>, 0];
215        }
216        #[cfg(feature = $cap )]
217        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
218        impl<'o> core::ops::$trait<&'o $W<$T>> for $W<$T> {
219            $crate::num::impl_ops![@op_body $W($T), $fn, &'o $W<$T>, 0];
220        }
221        #[cfg(feature = $cap )]
222        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
223        impl<'s, 'o> core::ops::$trait<&'o $W<$T>> for &'s $W<$T> {
224            $crate::num::impl_ops![@op_body $W($T), $fn, &'o $W<$T>, 0];
225        }
226        /* $W<$T> op $T -> $W<$T> */
227        #[cfg(feature = $cap )]
228        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
229        impl core::ops::$trait<$T> for $W<$T> {
230            $crate::num::impl_ops![@op_body $W($T), $fn, $T];
231        }
232        #[cfg(feature = $cap )]
233        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
234        impl<'s> core::ops::$trait<$T> for &'s $W<$T> {
235            $crate::num::impl_ops![@op_body $W($T), $fn, $T];
236        }
237        #[cfg(feature = $cap )]
238        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
239        impl<'o> core::ops::$trait<&'o $T> for $W<$T> {
240            $crate::num::impl_ops![@op_body $W($T), $fn, &'o $T];
241        }
242        #[cfg(feature = $cap )]
243        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
244        impl<'s, 'o> core::ops::$trait<&'o $T> for &'s $W<$T> {
245            $crate::num::impl_ops![@op_body $W($T), $fn, &'o $T];
246        }
247    };
248    (@op_body $W:ident($T:ty), $fn:ident, $other:ty $(, $other_field:tt)?) => {
249        type Output = $W<$T>;
250        fn $fn(self, other: $other) -> $W<$T> { $W(self.0.$fn(other$(. $other_field)?)) }
251    };
252
253    (@op_assign $W:ident($T:ty : $cap:literal), $trait:ident, $fn:ident) => { $crate::paste! {
254        /* $W<$T> op_assign $W<$T> */
255        #[cfg(feature = $cap )]
256        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
257        impl core::ops::$trait for $W<$T> {
258            fn $fn(&mut self, other: $W<$T>) { self.0.$fn(other.0); }
259        }
260        #[cfg(feature = $cap )]
261        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
262        impl<'o> core::ops::$trait<&'o $W<$T>> for $W<$T> {
263            fn $fn(&mut self, other: &'o $W<$T>) { self.0.$fn(other.0); }
264        }
265        /* $W<$T> op_assign $T -> $W<$T> */
266        #[cfg(feature = $cap )]
267        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
268        impl core::ops::$trait<$T> for $W<$T> {
269            fn $fn(&mut self, other: $T) { self.0.$fn(other); }
270        }
271        #[cfg(feature = $cap )]
272        #[cfg_attr(feature = "nightly_doc", doc(cfg(feature = $cap)))]
273        impl<'o> core::ops::$trait<&'o $T> for $W<$T> {
274            fn $fn(&mut self, other: &'o $T) { self.0.$fn(other); }
275        }
276    }};
277}
278pub(crate) use impl_ops;