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