nam_num_traits/ops/
euclid.rs

1pub trait Euclid: Sized {
2    /// Calculates Euclidean division, the matching method for `rem_euclid`.
3    ///
4    /// This computes the integer `n` such that
5    /// `self = n * v + self.rem_euclid(v)`.
6    /// In other words, the result is `self / v` rounded to the integer `n`
7    /// such that `self >= n * v`.
8    ///
9    /// # Examples
10    ///
11    /// ```
12    /// use num_traits::Euclid;
13    ///
14    /// let a: i32 = 7;
15    /// let b: i32 = 4;
16    /// assert_eq!(Euclid::div_euclid(&a, &b), 1); // 7 > 4 * 1
17    /// assert_eq!(Euclid::div_euclid(&-a, &b), -2); // -7 >= 4 * -2
18    /// assert_eq!(Euclid::div_euclid(&a, &-b), -1); // 7 >= -4 * -1
19    /// assert_eq!(Euclid::div_euclid(&-a, &-b), 2); // -7 >= -4 * 2
20    /// ```
21    fn div_euclid(&self, v: &Self) -> Self;
22
23    /// Calculates the least nonnegative remainder of `self (mod v)`.
24    ///
25    /// In particular, the return value `r` satisfies `0.0 <= r < v.abs()` in
26    /// most cases. However, due to a floating point round-off error it can
27    /// result in `r == v.abs()`, violating the mathematical definition, if
28    /// `self` is much smaller than `v.abs()` in magnitude and `self < 0.0`.
29    /// This result is not an element of the function's codomain, but it is the
30    /// closest floating point number in the real numbers and thus fulfills the
31    /// property `self == self.div_euclid(v) * v + self.rem_euclid(v)`
32    /// approximatively.
33    ///
34    /// # Examples
35    ///
36    /// ```
37    /// use num_traits::Euclid;
38    ///
39    /// let a: i32 = 7;
40    /// let b: i32 = 4;
41    /// assert_eq!(Euclid::rem_euclid(&a, &b), 3);
42    /// assert_eq!(Euclid::rem_euclid(&-a, &b), 1);
43    /// assert_eq!(Euclid::rem_euclid(&a, &-b), 3);
44    /// assert_eq!(Euclid::rem_euclid(&-a, &-b), 1);
45    /// ```
46    fn rem_euclid(&self, v: &Self) -> Self;
47
48    /// Returns both the quotient and remainder from Euclidean division.
49    ///
50    /// By default, it internally calls both `Euclid::div_euclid` and `Euclid::rem_euclid`,
51    /// but it can be overridden in order to implement some optimization.
52    ///
53    /// # Examples
54    ///
55    /// ```
56    /// # use num_traits::Euclid;
57    /// let x = 5u8;
58    /// let y = 3u8;
59    ///
60    /// let div = Euclid::div_euclid(&x, &y);
61    /// let rem = Euclid::rem_euclid(&x, &y);
62    ///
63    /// assert_eq!((div, rem), Euclid::div_rem_euclid(&x, &y));
64    /// ```
65    fn div_rem_euclid(&self, v: &Self) -> (Self, Self) {
66        (self.div_euclid(v), self.rem_euclid(v))
67    }
68}
69
70macro_rules! euclid_forward_impl {
71    ($($t:ty)*) => {$(
72        impl Euclid for $t {
73            #[inline]
74            fn div_euclid(&self, v: &$t) -> Self {
75                <$t>::div_euclid(*self, *v)
76            }
77
78            #[inline]
79            fn rem_euclid(&self, v: &$t) -> Self {
80                <$t>::rem_euclid(*self, *v)
81            }
82        }
83    )*}
84}
85
86euclid_forward_impl!(isize i8 i16 i32 i64 i128);
87euclid_forward_impl!(usize u8 u16 u32 u64 u128);
88
89#[cfg(feature = "std")]
90euclid_forward_impl!(f32 f64);
91
92#[cfg(not(feature = "std"))]
93impl Euclid for f32 {
94    #[inline]
95    fn div_euclid(&self, v: &f32) -> f32 {
96        let q = <f32 as crate::float::FloatCore>::trunc(self / v);
97        if self % v < 0.0 {
98            return if *v > 0.0 { q - 1.0 } else { q + 1.0 };
99        }
100        q
101    }
102
103    #[inline]
104    fn rem_euclid(&self, v: &f32) -> f32 {
105        let r = self % v;
106        if r < 0.0 {
107            r + <f32 as crate::float::FloatCore>::abs(*v)
108        } else {
109            r
110        }
111    }
112}
113
114#[cfg(not(feature = "std"))]
115impl Euclid for f64 {
116    #[inline]
117    fn div_euclid(&self, v: &f64) -> f64 {
118        let q = <f64 as crate::float::FloatCore>::trunc(self / v);
119        if self % v < 0.0 {
120            return if *v > 0.0 { q - 1.0 } else { q + 1.0 };
121        }
122        q
123    }
124
125    #[inline]
126    fn rem_euclid(&self, v: &f64) -> f64 {
127        let r = self % v;
128        if r < 0.0 {
129            r + <f64 as crate::float::FloatCore>::abs(*v)
130        } else {
131            r
132        }
133    }
134}
135
136pub trait CheckedEuclid: Euclid {
137    /// Performs euclid division, returning `None` on division by zero or if
138    /// overflow occurred.
139    fn checked_div_euclid(&self, v: &Self) -> Option<Self>;
140
141    /// Finds the euclid remainder of dividing two numbers, returning `None` on
142    /// division by zero or if overflow occurred.
143    fn checked_rem_euclid(&self, v: &Self) -> Option<Self>;
144
145    /// Returns both the quotient and remainder from checked Euclidean division,
146    /// returning `None` on division by zero or if overflow occurred.
147    ///
148    /// By default, it internally calls both `CheckedEuclid::checked_div_euclid` and `CheckedEuclid::checked_rem_euclid`,
149    /// but it can be overridden in order to implement some optimization.
150    /// # Examples
151    ///
152    /// ```
153    /// # use num_traits::CheckedEuclid;
154    /// let x = 5u8;
155    /// let y = 3u8;
156    ///
157    /// let div = CheckedEuclid::checked_div_euclid(&x, &y);
158    /// let rem = CheckedEuclid::checked_rem_euclid(&x, &y);
159    ///
160    /// assert_eq!(Some((div.unwrap(), rem.unwrap())), CheckedEuclid::checked_div_rem_euclid(&x, &y));
161    /// ```
162    fn checked_div_rem_euclid(&self, v: &Self) -> Option<(Self, Self)> {
163        Some((self.checked_div_euclid(v)?, self.checked_rem_euclid(v)?))
164    }
165}
166
167macro_rules! checked_euclid_forward_impl {
168    ($($t:ty)*) => {$(
169        impl CheckedEuclid for $t {
170            #[inline]
171            fn checked_div_euclid(&self, v: &$t) -> Option<Self> {
172                <$t>::checked_div_euclid(*self, *v)
173            }
174
175            #[inline]
176            fn checked_rem_euclid(&self, v: &$t) -> Option<Self> {
177                <$t>::checked_rem_euclid(*self, *v)
178            }
179        }
180    )*}
181}
182
183checked_euclid_forward_impl!(isize i8 i16 i32 i64 i128);
184checked_euclid_forward_impl!(usize u8 u16 u32 u64 u128);
185
186#[cfg(test)]
187mod tests {
188    use super::*;
189
190    #[test]
191    fn euclid_unsigned() {
192        macro_rules! test_euclid {
193            ($($t:ident)+) => {
194                $(
195                    {
196                        let x: $t = 10;
197                        let y: $t = 3;
198                        let div = Euclid::div_euclid(&x, &y);
199                        let rem = Euclid::rem_euclid(&x, &y);
200                        assert_eq!(div, 3);
201                        assert_eq!(rem, 1);
202                        assert_eq!((div, rem), Euclid::div_rem_euclid(&x, &y));
203                    }
204                )+
205            };
206        }
207
208        test_euclid!(usize u8 u16 u32 u64);
209    }
210
211    #[test]
212    fn euclid_signed() {
213        macro_rules! test_euclid {
214            ($($t:ident)+) => {
215                $(
216                    {
217                        let x: $t = 10;
218                        let y: $t = -3;
219                        assert_eq!(Euclid::div_euclid(&x, &y), -3);
220                        assert_eq!(Euclid::div_euclid(&-x, &y), 4);
221                        assert_eq!(Euclid::rem_euclid(&x, &y), 1);
222                        assert_eq!(Euclid::rem_euclid(&-x, &y), 2);
223                        assert_eq!((Euclid::div_euclid(&x, &y), Euclid::rem_euclid(&x, &y)), Euclid::div_rem_euclid(&x, &y));
224                        let x: $t = $t::min_value() + 1;
225                        let y: $t = -1;
226                        assert_eq!(Euclid::div_euclid(&x, &y), $t::max_value());
227                    }
228                )+
229            };
230        }
231
232        test_euclid!(isize i8 i16 i32 i64 i128);
233    }
234
235    #[test]
236    fn euclid_float() {
237        macro_rules! test_euclid {
238            ($($t:ident)+) => {
239                $(
240                    {
241                        let x: $t = 12.1;
242                        let y: $t = 3.2;
243                        assert!(Euclid::div_euclid(&x, &y) * y + Euclid::rem_euclid(&x, &y) - x
244                        <= 46.4 * <$t as crate::float::FloatCore>::epsilon());
245                        assert!(Euclid::div_euclid(&x, &-y) * -y + Euclid::rem_euclid(&x, &-y) - x
246                        <= 46.4 * <$t as crate::float::FloatCore>::epsilon());
247                        assert!(Euclid::div_euclid(&-x, &y) * y + Euclid::rem_euclid(&-x, &y) + x
248                        <= 46.4 * <$t as crate::float::FloatCore>::epsilon());
249                        assert!(Euclid::div_euclid(&-x, &-y) * -y + Euclid::rem_euclid(&-x, &-y) + x
250                        <= 46.4 * <$t as crate::float::FloatCore>::epsilon());
251                        assert_eq!((Euclid::div_euclid(&x, &y), Euclid::rem_euclid(&x, &y)), Euclid::div_rem_euclid(&x, &y));
252                    }
253                )+
254            };
255        }
256
257        test_euclid!(f32 f64);
258    }
259
260    #[test]
261    fn euclid_checked() {
262        macro_rules! test_euclid_checked {
263            ($($t:ident)+) => {
264                $(
265                    {
266                        assert_eq!(CheckedEuclid::checked_div_euclid(&$t::min_value(), &-1), None);
267                        assert_eq!(CheckedEuclid::checked_rem_euclid(&$t::min_value(), &-1), None);
268                        assert_eq!(CheckedEuclid::checked_div_euclid(&1, &0), None);
269                        assert_eq!(CheckedEuclid::checked_rem_euclid(&1, &0), None);
270                    }
271                )+
272            };
273        }
274
275        test_euclid_checked!(isize i8 i16 i32 i64 i128);
276    }
277}