Skip to main content

const_num_traits/ops/
euclid.rs

1//! Euclidean division and remainder.
2//!
3//! **CT tier C (CT-hostile)**: division is data-dependent on every
4//! mainstream CPU; constant-time types should not implement these.
5
6c0nst::c0nst! {
7pub c0nst trait Euclid: Sized {
8    /// The result type of Euclidean division and remainder (`Self` for the
9    /// primitive and float impls).
10    type Output;
11
12    /// Calculates Euclidean division, the matching method for `rem_euclid`.
13    ///
14    /// This computes the integer `n` such that
15    /// `self = n * v + self.rem_euclid(v)`.
16    /// In other words, the result is `self / v` rounded to the integer `n`
17    /// such that `self >= n * v`.
18    ///
19    /// # Examples
20    ///
21    /// ```
22    /// use const_num_traits::Euclid;
23    ///
24    /// let a: i32 = 7;
25    /// let b: i32 = 4;
26    /// assert_eq!(Euclid::div_euclid(a, b), 1); // 7 > 4 * 1
27    /// assert_eq!(Euclid::div_euclid(-a, b), -2); // -7 >= 4 * -2
28    /// assert_eq!(Euclid::div_euclid(a, -b), -1); // 7 >= -4 * -1
29    /// assert_eq!(Euclid::div_euclid(-a, -b), 2); // -7 >= -4 * 2
30    /// ```
31    fn div_euclid(self, v: Self) -> Self::Output;
32
33    /// Calculates the least nonnegative remainder of `self (mod v)`.
34    ///
35    /// In particular, the return value `r` satisfies `0.0 <= r < v.abs()` in
36    /// most cases. However, due to a floating point round-off error it can
37    /// result in `r == v.abs()`, violating the mathematical definition, if
38    /// `self` is much smaller than `v.abs()` in magnitude and `self < 0.0`.
39    /// This result is not an element of the function's codomain, but it is the
40    /// closest floating point number in the real numbers and thus fulfills the
41    /// property `self == self.div_euclid(v) * v + self.rem_euclid(v)`
42    /// approximatively.
43    ///
44    /// # Examples
45    ///
46    /// ```
47    /// use const_num_traits::Euclid;
48    ///
49    /// let a: i32 = 7;
50    /// let b: i32 = 4;
51    /// assert_eq!(Euclid::rem_euclid(a, b), 3);
52    /// assert_eq!(Euclid::rem_euclid(-a, b), 1);
53    /// assert_eq!(Euclid::rem_euclid(a, -b), 3);
54    /// assert_eq!(Euclid::rem_euclid(-a, -b), 1);
55    /// ```
56    fn rem_euclid(self, v: Self) -> Self::Output;
57
58    /// Returns both the quotient and remainder from Euclidean division.
59    ///
60    /// By default, it internally calls both `Euclid::div_euclid` and `Euclid::rem_euclid`,
61    /// but it can be overridden in order to implement some optimization.
62    ///
63    /// # Examples
64    ///
65    /// ```
66    /// # use const_num_traits::Euclid;
67    /// let x = 5u8;
68    /// let y = 3u8;
69    ///
70    /// let div = Euclid::div_euclid(x, y);
71    /// let rem = Euclid::rem_euclid(x, y);
72    ///
73    /// assert_eq!((div, rem), Euclid::div_rem_euclid(x, y));
74    /// ```
75    fn div_rem_euclid(self, v: Self) -> (Self::Output, Self::Output);
76}
77}
78
79macro_rules! euclid_forward_impl {
80    ($($t:ty)*) => {$(
81        c0nst::c0nst! {
82        c0nst impl Euclid for $t {
83            type Output = $t;
84            #[inline]
85            fn div_euclid(self, v: $t) -> Self {
86                <$t>::div_euclid(self, v)
87            }
88
89            #[inline]
90            fn rem_euclid(self, v: $t) -> Self {
91                <$t>::rem_euclid(self, v)
92            }
93
94            #[inline]
95            fn div_rem_euclid(self, v: $t) -> ($t, $t) {
96                (<$t>::div_euclid(self, v), <$t>::rem_euclid(self, v))
97            }
98        }
99        }
100    )*}
101}
102
103euclid_forward_impl!(isize i8 i16 i32 i64 i128);
104euclid_forward_impl!(usize u8 u16 u32 u64 u128);
105
106// f32/f64 keep plain (non-const) impls: their inherent `div_euclid`/`rem_euclid`
107// aren't const fns yet, so a separate non-const macro path is used.
108#[cfg(feature = "std")]
109macro_rules! euclid_forward_impl_float {
110    ($($t:ty)*) => {$(
111        impl Euclid for $t {
112            type Output = $t;
113            #[inline]
114            fn div_euclid(self, v: $t) -> Self {
115                <$t>::div_euclid(self, v)
116            }
117
118            #[inline]
119            fn rem_euclid(self, v: $t) -> Self {
120                <$t>::rem_euclid(self, v)
121            }
122
123            #[inline]
124            fn div_rem_euclid(self, v: $t) -> ($t, $t) {
125                (<$t>::div_euclid(self, v), <$t>::rem_euclid(self, v))
126            }
127        }
128    )*}
129}
130
131#[cfg(feature = "std")]
132euclid_forward_impl_float!(f32 f64);
133
134#[cfg(not(feature = "std"))]
135impl Euclid for f32 {
136    type Output = f32;
137    #[inline]
138    fn div_euclid(self, v: f32) -> f32 {
139        let q = <f32 as crate::float::FloatCore>::trunc(self / v);
140        if self % v < 0.0 {
141            return if v > 0.0 { q - 1.0 } else { q + 1.0 };
142        }
143        q
144    }
145
146    #[inline]
147    fn rem_euclid(self, v: f32) -> f32 {
148        let r = self % v;
149        if r < 0.0 {
150            r + <f32 as crate::float::FloatCore>::abs(v)
151        } else {
152            r
153        }
154    }
155
156    #[inline]
157    fn div_rem_euclid(self, v: f32) -> (f32, f32) {
158        (Euclid::div_euclid(self, v), Euclid::rem_euclid(self, v))
159    }
160}
161
162#[cfg(not(feature = "std"))]
163impl Euclid for f64 {
164    type Output = f64;
165    #[inline]
166    fn div_euclid(self, v: f64) -> f64 {
167        let q = <f64 as crate::float::FloatCore>::trunc(self / v);
168        if self % v < 0.0 {
169            return if v > 0.0 { q - 1.0 } else { q + 1.0 };
170        }
171        q
172    }
173
174    #[inline]
175    fn rem_euclid(self, v: f64) -> f64 {
176        let r = self % v;
177        if r < 0.0 {
178            r + <f64 as crate::float::FloatCore>::abs(v)
179        } else {
180            r
181        }
182    }
183
184    #[inline]
185    fn div_rem_euclid(self, v: f64) -> (f64, f64) {
186        (Euclid::div_euclid(self, v), Euclid::rem_euclid(self, v))
187    }
188}
189
190c0nst::c0nst! {
191pub c0nst trait CheckedEuclid: [c0nst] Euclid {
192    /// Performs euclid division, returning `None` on division by zero or if
193    /// overflow occurred.
194    fn checked_div_euclid(self, v: Self) -> Option<<Self as Euclid>::Output>;
195
196    /// Finds the euclid remainder of dividing two numbers, returning `None` on
197    /// division by zero or if overflow occurred.
198    fn checked_rem_euclid(self, v: Self) -> Option<<Self as Euclid>::Output>;
199
200    /// Returns both the quotient and remainder from checked Euclidean division,
201    /// returning `None` on division by zero or if overflow occurred.
202    ///
203    /// # Examples
204    ///
205    /// ```
206    /// # use const_num_traits::CheckedEuclid;
207    /// let x = 5u8;
208    /// let y = 3u8;
209    ///
210    /// let div = CheckedEuclid::checked_div_euclid(x, y);
211    /// let rem = CheckedEuclid::checked_rem_euclid(x, y);
212    ///
213    /// assert_eq!(Some((div.unwrap(), rem.unwrap())), CheckedEuclid::checked_div_rem_euclid(x, y));
214    /// ```
215    fn checked_div_rem_euclid(self, v: Self) -> Option<(<Self as Euclid>::Output, <Self as Euclid>::Output)>;
216}
217}
218
219macro_rules! checked_euclid_forward_impl {
220    ($($t:ty)*) => {$(
221        c0nst::c0nst! {
222        c0nst impl CheckedEuclid for $t {
223            #[inline]
224            fn checked_div_euclid(self, v: $t) -> Option<Self> {
225                <$t>::checked_div_euclid(self, v)
226            }
227
228            #[inline]
229            fn checked_rem_euclid(self, v: $t) -> Option<Self> {
230                <$t>::checked_rem_euclid(self, v)
231            }
232
233            // `?` desugars through `Try`/`FromResidual` which aren't const;
234            // hand-roll the bail-out via `match`.
235            #[inline]
236            fn checked_div_rem_euclid(self, v: $t) -> Option<(Self, Self)> {
237                let d = match <$t>::checked_div_euclid(self, v) {
238                    Some(x) => x,
239                    None => return None,
240                };
241                let r = match <$t>::checked_rem_euclid(self, v) {
242                    Some(x) => x,
243                    None => return None,
244                };
245                Some((d, r))
246            }
247        }
248        }
249    )*}
250}
251
252checked_euclid_forward_impl!(isize i8 i16 i32 i64 i128);
253checked_euclid_forward_impl!(usize u8 u16 u32 u64 u128);
254
255c0nst::c0nst! {
256/// Performs Euclidean division and remainder that wrap around on overflow.
257pub c0nst trait WrappingEuclid: [c0nst] Euclid {
258    /// Wrapping Euclidean division. Computes `Euclid::div_euclid(self, v)`,
259    /// wrapping around at the boundary of the type. The only wrapping case is
260    /// `MIN / -1` on a signed type.
261    ///
262    /// # Panics
263    ///
264    /// Panics if `v` is zero.
265    fn wrapping_div_euclid(self, v: Self) -> <Self as Euclid>::Output;
266
267    /// Wrapping Euclidean remainder. Computes `Euclid::rem_euclid(self, v)`,
268    /// wrapping around at the boundary of the type. The only wrapping case is
269    /// `MIN % -1` on a signed type, where the remainder is 0.
270    ///
271    /// # Panics
272    ///
273    /// Panics if `v` is zero.
274    fn wrapping_rem_euclid(self, v: Self) -> <Self as Euclid>::Output;
275}
276}
277
278macro_rules! wrapping_euclid_forward_impl {
279    ($($t:ty)*) => {$(
280        c0nst::c0nst! {
281        c0nst impl WrappingEuclid for $t {
282            #[inline]
283            fn wrapping_div_euclid(self, v: $t) -> Self {
284                <$t>::wrapping_div_euclid(self, v)
285            }
286
287            #[inline]
288            fn wrapping_rem_euclid(self, v: $t) -> Self {
289                <$t>::wrapping_rem_euclid(self, v)
290            }
291        }
292        }
293    )*}
294}
295
296wrapping_euclid_forward_impl!(isize i8 i16 i32 i64 i128);
297wrapping_euclid_forward_impl!(usize u8 u16 u32 u64 u128);
298
299c0nst::c0nst! {
300/// Performs Euclidean division and remainder with a flag for overflow.
301pub c0nst trait OverflowingEuclid: [c0nst] Euclid {
302    /// Returns a tuple of the Euclidean quotient along with a boolean
303    /// indicating whether an arithmetic overflow would occur. The only
304    /// overflowing case is `MIN / -1` on a signed type.
305    ///
306    /// # Panics
307    ///
308    /// Panics if `v` is zero.
309    fn overflowing_div_euclid(self, v: Self) -> (<Self as Euclid>::Output, bool);
310
311    /// Returns a tuple of the Euclidean remainder along with a boolean
312    /// indicating whether an arithmetic overflow would occur. The only
313    /// overflowing case is `MIN % -1` on a signed type, where the remainder
314    /// is 0.
315    ///
316    /// # Panics
317    ///
318    /// Panics if `v` is zero.
319    fn overflowing_rem_euclid(self, v: Self) -> (<Self as Euclid>::Output, bool);
320}
321}
322
323macro_rules! overflowing_euclid_forward_impl {
324    ($($t:ty)*) => {$(
325        c0nst::c0nst! {
326        c0nst impl OverflowingEuclid for $t {
327            #[inline]
328            fn overflowing_div_euclid(self, v: $t) -> (Self, bool) {
329                <$t>::overflowing_div_euclid(self, v)
330            }
331
332            #[inline]
333            fn overflowing_rem_euclid(self, v: $t) -> (Self, bool) {
334                <$t>::overflowing_rem_euclid(self, v)
335            }
336        }
337        }
338    )*}
339}
340
341overflowing_euclid_forward_impl!(isize i8 i16 i32 i64 i128);
342overflowing_euclid_forward_impl!(usize u8 u16 u32 u64 u128);
343
344#[cfg(test)]
345mod tests {
346    use super::*;
347
348    #[test]
349    fn euclid_unsigned() {
350        macro_rules! test_euclid {
351            ($($t:ident)+) => {
352                $(
353                    {
354                        let x: $t = 10;
355                        let y: $t = 3;
356                        let div = Euclid::div_euclid(x, y);
357                        let rem = Euclid::rem_euclid(x, y);
358                        assert_eq!(div, 3);
359                        assert_eq!(rem, 1);
360                        assert_eq!((div, rem), Euclid::div_rem_euclid(x, y));
361                    }
362                )+
363            };
364        }
365
366        test_euclid!(usize u8 u16 u32 u64);
367    }
368
369    #[test]
370    fn euclid_signed() {
371        macro_rules! test_euclid {
372            ($($t:ident)+) => {
373                $(
374                    {
375                        let x: $t = 10;
376                        let y: $t = -3;
377                        assert_eq!(Euclid::div_euclid(x, y), -3);
378                        assert_eq!(Euclid::div_euclid(-x, y), 4);
379                        assert_eq!(Euclid::rem_euclid(x, y), 1);
380                        assert_eq!(Euclid::rem_euclid(-x, y), 2);
381                        assert_eq!((Euclid::div_euclid(x, y), Euclid::rem_euclid(x, y)), Euclid::div_rem_euclid(x, y));
382                        let x: $t = $t::MIN + 1;
383                        let y: $t = -1;
384                        assert_eq!(Euclid::div_euclid(x, y), $t::MAX);
385                    }
386                )+
387            };
388        }
389
390        test_euclid!(isize i8 i16 i32 i64 i128);
391    }
392
393    #[test]
394    #[cfg(feature = "std")]
395    fn euclid_float() {
396        macro_rules! test_euclid {
397            ($($t:ident)+) => {
398                $(
399                    {
400                        let x: $t = 12.1;
401                        let y: $t = 3.2;
402                        // Absolute reconstruction error, so a large *negative*
403                        // residual can't slip past the `<= eps` bound.
404                        assert!((Euclid::div_euclid(x, y) * y + Euclid::rem_euclid(x, y) - x).abs()
405                        <= 46.4 * <$t>::EPSILON);
406                        assert!((Euclid::div_euclid(x, -y) * -y + Euclid::rem_euclid(x, -y) - x).abs()
407                        <= 46.4 * <$t>::EPSILON);
408                        assert!((Euclid::div_euclid(-x, y) * y + Euclid::rem_euclid(-x, y) + x).abs()
409                        <= 46.4 * <$t>::EPSILON);
410                        assert!((Euclid::div_euclid(-x, -y) * -y + Euclid::rem_euclid(-x, -y) + x).abs()
411                        <= 46.4 * <$t>::EPSILON);
412                        assert_eq!((Euclid::div_euclid(x, y), Euclid::rem_euclid(x, y)), Euclid::div_rem_euclid(x, y));
413                    }
414                )+
415            };
416        }
417
418        test_euclid!(f32 f64);
419    }
420
421    #[test]
422    fn euclid_wrapping_overflowing() {
423        assert_eq!(WrappingEuclid::wrapping_div_euclid(i8::MIN, -1), i8::MIN);
424        assert_eq!(WrappingEuclid::wrapping_rem_euclid(i8::MIN, -1), 0);
425        assert_eq!(WrappingEuclid::wrapping_div_euclid(-7i32, 4), -2);
426        assert_eq!(WrappingEuclid::wrapping_rem_euclid(-7i32, 4), 1);
427        assert_eq!(
428            OverflowingEuclid::overflowing_div_euclid(i8::MIN, -1),
429            (i8::MIN, true)
430        );
431        assert_eq!(
432            OverflowingEuclid::overflowing_rem_euclid(i8::MIN, -1),
433            (0, true)
434        );
435        assert_eq!(
436            OverflowingEuclid::overflowing_div_euclid(7u8, 4),
437            (1, false)
438        );
439    }
440
441    #[test]
442    fn euclid_checked() {
443        macro_rules! test_euclid_checked {
444            ($($t:ident)+) => {
445                $(
446                    {
447                        assert_eq!(CheckedEuclid::checked_div_euclid($t::MIN, -1), None);
448                        assert_eq!(CheckedEuclid::checked_rem_euclid($t::MIN, -1), None);
449                        assert_eq!(CheckedEuclid::checked_div_euclid(1, 0), None);
450                        assert_eq!(CheckedEuclid::checked_rem_euclid(1, 0), None);
451                    }
452                )+
453            };
454        }
455
456        test_euclid_checked!(isize i8 i16 i32 i64 i128);
457    }
458}