arithmetic_eval/arith/
generic.rs

1//! Generic arithmetics.
2
3use num_traits::{
4    checked_pow, CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedSub, NumOps, One, Pow,
5    Signed, Unsigned, WrappingAdd, WrappingMul, WrappingNeg, WrappingSub, Zero,
6};
7
8use core::{cmp::Ordering, convert::TryFrom, marker::PhantomData, ops};
9
10use crate::{
11    arith::{Arithmetic, OrdArithmetic},
12    error::ArithmeticError,
13};
14
15/// Arithmetic on a number type that implements all necessary operations natively.
16///
17/// As an example, this type implements [`Arithmetic`] for `f32`, `f64`, and the floating-point
18/// complex numbers from the [`num-complex`] crate.
19///
20/// [`num-complex`]: https://crates.io/crates/num-complex/
21#[derive(Debug, Clone, Copy, Default)]
22pub struct StdArithmetic;
23
24impl<T> Arithmetic<T> for StdArithmetic
25where
26    T: Clone + NumOps + PartialEq + ops::Neg<Output = T> + Pow<T, Output = T>,
27{
28    #[inline]
29    fn add(&self, x: T, y: T) -> Result<T, ArithmeticError> {
30        Ok(x + y)
31    }
32
33    #[inline]
34    fn sub(&self, x: T, y: T) -> Result<T, ArithmeticError> {
35        Ok(x - y)
36    }
37
38    #[inline]
39    fn mul(&self, x: T, y: T) -> Result<T, ArithmeticError> {
40        Ok(x * y)
41    }
42
43    #[inline]
44    fn div(&self, x: T, y: T) -> Result<T, ArithmeticError> {
45        Ok(x / y)
46    }
47
48    #[inline]
49    fn pow(&self, x: T, y: T) -> Result<T, ArithmeticError> {
50        Ok(x.pow(y))
51    }
52
53    #[inline]
54    fn neg(&self, x: T) -> Result<T, ArithmeticError> {
55        Ok(-x)
56    }
57
58    #[inline]
59    fn eq(&self, x: &T, y: &T) -> bool {
60        *x == *y
61    }
62}
63
64impl<T> OrdArithmetic<T> for StdArithmetic
65where
66    Self: Arithmetic<T>,
67    T: PartialOrd,
68{
69    fn partial_cmp(&self, x: &T, y: &T) -> Option<Ordering> {
70        x.partial_cmp(y)
71    }
72}
73
74#[cfg(all(test, feature = "std"))]
75static_assertions::assert_impl_all!(StdArithmetic: OrdArithmetic<f32>, OrdArithmetic<f64>);
76
77#[cfg(all(test, feature = "complex"))]
78static_assertions::assert_impl_all!(
79    StdArithmetic: Arithmetic<num_complex::Complex32>,
80    Arithmetic<num_complex::Complex64>
81);
82
83/// Helper trait for [`CheckedArithmetic`] describing how number negation should be implemented.
84pub trait CheckedArithmeticKind<T> {
85    /// Negates the provided `value`, or returns `None` if the value cannot be negated.
86    fn checked_neg(value: T) -> Option<T>;
87}
88
89/// Arithmetic on an integer type (e.g., `i32`) that checks overflow and other failure
90/// conditions for all operations.
91///
92/// As an example, this type implements [`Arithmetic`] for all built-in integer types
93/// with a definite size (`u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`, `u128`, `i128`).
94///
95/// The type param defines how negation should be performed; it should be one of [`Checked`]
96/// (default value), [`Unchecked`] or [`NegateOnlyZero`]. See the docs for these types for
97/// more details.
98#[derive(Debug)]
99pub struct CheckedArithmetic<Kind = Checked>(PhantomData<Kind>);
100
101impl<Kind> Clone for CheckedArithmetic<Kind> {
102    fn clone(&self) -> Self {
103        Self(self.0)
104    }
105}
106
107impl<Kind> Copy for CheckedArithmetic<Kind> {}
108
109impl<Kind> Default for CheckedArithmetic<Kind> {
110    fn default() -> Self {
111        Self(PhantomData)
112    }
113}
114
115impl<Kind> CheckedArithmetic<Kind> {
116    /// Creates a new arithmetic instance.
117    pub const fn new() -> Self {
118        Self(PhantomData)
119    }
120}
121
122impl<T, Kind> Arithmetic<T> for CheckedArithmetic<Kind>
123where
124    T: Clone + PartialEq + Zero + One + CheckedAdd + CheckedSub + CheckedMul + CheckedDiv,
125    Kind: CheckedArithmeticKind<T>,
126    usize: TryFrom<T>,
127{
128    #[inline]
129    fn add(&self, x: T, y: T) -> Result<T, ArithmeticError> {
130        x.checked_add(&y).ok_or(ArithmeticError::IntegerOverflow)
131    }
132
133    #[inline]
134    fn sub(&self, x: T, y: T) -> Result<T, ArithmeticError> {
135        x.checked_sub(&y).ok_or(ArithmeticError::IntegerOverflow)
136    }
137
138    #[inline]
139    fn mul(&self, x: T, y: T) -> Result<T, ArithmeticError> {
140        x.checked_mul(&y).ok_or(ArithmeticError::IntegerOverflow)
141    }
142
143    #[inline]
144    fn div(&self, x: T, y: T) -> Result<T, ArithmeticError> {
145        x.checked_div(&y).ok_or(ArithmeticError::DivisionByZero)
146    }
147
148    #[inline]
149    #[allow(clippy::map_err_ignore)]
150    fn pow(&self, x: T, y: T) -> Result<T, ArithmeticError> {
151        let exp = usize::try_from(y).map_err(|_| ArithmeticError::InvalidExponent)?;
152        checked_pow(x, exp).ok_or(ArithmeticError::IntegerOverflow)
153    }
154
155    #[inline]
156    fn neg(&self, x: T) -> Result<T, ArithmeticError> {
157        Kind::checked_neg(x).ok_or(ArithmeticError::IntegerOverflow)
158    }
159
160    #[inline]
161    fn eq(&self, x: &T, y: &T) -> bool {
162        *x == *y
163    }
164}
165
166/// Marker for [`CheckedArithmetic`] signalling that negation should be inherited
167/// from the [`CheckedNeg`] trait.
168#[derive(Debug)]
169pub struct Checked(());
170
171impl<T: CheckedNeg> CheckedArithmeticKind<T> for Checked {
172    fn checked_neg(value: T) -> Option<T> {
173        value.checked_neg()
174    }
175}
176
177/// Marker for [`CheckedArithmetic`] signalling that negation is only possible for zero.
178#[derive(Debug)]
179pub struct NegateOnlyZero(());
180
181impl<T: Unsigned + Zero> CheckedArithmeticKind<T> for NegateOnlyZero {
182    fn checked_neg(value: T) -> Option<T> {
183        if value.is_zero() {
184            Some(value)
185        } else {
186            None
187        }
188    }
189}
190
191/// Marker for [`CheckedArithmetic`] signalling that negation should be inherited from
192/// the [`Neg`](ops::Neg) trait. This is appropriate if `Neg` never panics (e.g.,
193/// for signed big integers).
194#[derive(Debug)]
195pub struct Unchecked(());
196
197impl<T: Signed> CheckedArithmeticKind<T> for Unchecked {
198    fn checked_neg(value: T) -> Option<T> {
199        Some(-value)
200    }
201}
202
203impl<T, Kind> OrdArithmetic<T> for CheckedArithmetic<Kind>
204where
205    Self: Arithmetic<T>,
206    T: PartialOrd,
207{
208    #[inline]
209    fn partial_cmp(&self, x: &T, y: &T) -> Option<Ordering> {
210        x.partial_cmp(y)
211    }
212}
213
214#[cfg(test)]
215static_assertions::assert_impl_all!(
216    CheckedArithmetic: OrdArithmetic<u8>,
217    OrdArithmetic<i8>,
218    OrdArithmetic<u16>,
219    OrdArithmetic<i16>,
220    OrdArithmetic<u32>,
221    OrdArithmetic<i32>,
222    OrdArithmetic<u64>,
223    OrdArithmetic<i64>,
224    OrdArithmetic<u128>,
225    OrdArithmetic<i128>
226);
227
228/// Arithmetic on an integer type (e.g., `i32`), in which all operations have wrapping semantics.
229///
230/// As an example, this type implements [`Arithmetic`] for all built-in integer types
231/// with a definite size (`u8`, `i8`, `u16`, `i16`, `u32`, `i32`, `u64`, `i64`, `u128`, `i128`).
232#[derive(Debug, Clone, Copy, Default)]
233pub struct WrappingArithmetic;
234
235impl<T> Arithmetic<T> for WrappingArithmetic
236where
237    T: Copy
238        + PartialEq
239        + Zero
240        + One
241        + WrappingAdd
242        + WrappingSub
243        + WrappingMul
244        + WrappingNeg
245        + ops::Div<T, Output = T>,
246    usize: TryFrom<T>,
247{
248    #[inline]
249    fn add(&self, x: T, y: T) -> Result<T, ArithmeticError> {
250        Ok(x.wrapping_add(&y))
251    }
252
253    #[inline]
254    fn sub(&self, x: T, y: T) -> Result<T, ArithmeticError> {
255        Ok(x.wrapping_sub(&y))
256    }
257
258    #[inline]
259    fn mul(&self, x: T, y: T) -> Result<T, ArithmeticError> {
260        Ok(x.wrapping_mul(&y))
261    }
262
263    #[inline]
264    fn div(&self, x: T, y: T) -> Result<T, ArithmeticError> {
265        if y.is_zero() {
266            Err(ArithmeticError::DivisionByZero)
267        } else if y.wrapping_neg().is_one() {
268            // Division by -1 is the only case when an overflow may occur. We just replace it
269            // with `wrapping_neg`.
270            Ok(x.wrapping_neg())
271        } else {
272            Ok(x / y)
273        }
274    }
275
276    #[inline]
277    #[allow(clippy::map_err_ignore)]
278    fn pow(&self, x: T, y: T) -> Result<T, ArithmeticError> {
279        let exp = usize::try_from(y).map_err(|_| ArithmeticError::InvalidExponent)?;
280        Ok(wrapping_exp(x, exp))
281    }
282
283    #[inline]
284    fn neg(&self, x: T) -> Result<T, ArithmeticError> {
285        Ok(x.wrapping_neg())
286    }
287
288    #[inline]
289    fn eq(&self, x: &T, y: &T) -> bool {
290        *x == *y
291    }
292}
293
294impl<T> OrdArithmetic<T> for WrappingArithmetic
295where
296    Self: Arithmetic<T>,
297    T: PartialOrd,
298{
299    #[inline]
300    fn partial_cmp(&self, x: &T, y: &T) -> Option<Ordering> {
301        x.partial_cmp(y)
302    }
303}
304
305// Refactored from `num_traits::pow()`:
306// https://docs.rs/num-traits/0.2.14/src/num_traits/pow.rs.html#189-211
307fn wrapping_exp<T: Copy + One + WrappingMul>(mut base: T, mut exp: usize) -> T {
308    if exp == 0 {
309        return T::one();
310    }
311
312    while exp & 1 == 0 {
313        base = base.wrapping_mul(&base);
314        exp >>= 1;
315    }
316    if exp == 1 {
317        return base;
318    }
319
320    let mut acc = base;
321    while exp > 1 {
322        exp >>= 1;
323        base = base.wrapping_mul(&base);
324        if exp & 1 == 1 {
325            acc = acc.wrapping_mul(&base);
326        }
327    }
328    acc
329}
330
331#[cfg(test)]
332static_assertions::assert_impl_all!(
333    WrappingArithmetic: OrdArithmetic<u8>,
334    OrdArithmetic<i8>,
335    OrdArithmetic<u16>,
336    OrdArithmetic<i16>,
337    OrdArithmetic<u32>,
338    OrdArithmetic<i32>,
339    OrdArithmetic<u64>,
340    OrdArithmetic<i64>,
341    OrdArithmetic<u128>,
342    OrdArithmetic<i128>
343);