malachite_base/num/arithmetic/
mod_shr.rs

1// Copyright © 2025 Mikhail Hogrefe
2//
3// This file is part of Malachite.
4//
5// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
6// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
7// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
8
9use crate::num::arithmetic::traits::{ModShl, ModShlAssign, ModShr, ModShrAssign, UnsignedAbs};
10use crate::num::basic::signeds::PrimitiveSigned;
11use crate::num::basic::unsigneds::PrimitiveUnsigned;
12use core::ops::{Shr, ShrAssign};
13
14fn mod_shr_signed<
15    T: ModShl<U, T, Output = T> + PrimitiveUnsigned + Shr<U, Output = T>,
16    U: PrimitiveUnsigned,
17    S: PrimitiveSigned + UnsignedAbs<Output = U>,
18>(
19    x: T,
20    other: S,
21    m: T,
22) -> T {
23    assert!(x < m, "x must be reduced mod m, but {x} >= {m}");
24    let other_abs = other.unsigned_abs();
25    if other >= S::ZERO {
26        let width = U::wrapping_from(T::WIDTH);
27        if width != U::ZERO && other_abs >= width {
28            T::ZERO
29        } else {
30            x >> other_abs
31        }
32    } else {
33        x.mod_shl(other_abs, m)
34    }
35}
36
37fn mod_shr_assign_signed<
38    T: ModShlAssign<U, T> + PrimitiveUnsigned + ShrAssign<U>,
39    U: PrimitiveUnsigned,
40    S: PrimitiveSigned + UnsignedAbs<Output = U>,
41>(
42    x: &mut T,
43    other: S,
44    m: T,
45) {
46    assert!(*x < m, "x must be reduced mod m, but {x} >= {m}");
47    let other_abs = other.unsigned_abs();
48    if other >= S::ZERO {
49        let width = U::wrapping_from(T::WIDTH);
50        if width != U::ZERO && other_abs >= width {
51            *x = T::ZERO;
52        } else {
53            *x >>= other_abs;
54        }
55    } else {
56        x.mod_shl_assign(other_abs, m);
57    }
58}
59
60macro_rules! impl_mod_shr_signed {
61    ($t:ident) => {
62        macro_rules! impl_mod_shr_signed_inner {
63            ($u:ident) => {
64                impl ModShr<$u, $t> for $t {
65                    type Output = $t;
66
67                    /// Right-shifts a number (divides it by a power of 2) modulo a number $m$. The
68                    /// number must be already reduced modulo $m$.
69                    ///
70                    /// $f(x, n, m) = y$, where $x, y < m$ and $\lfloor 2^{-n}x \rfloor \equiv y
71                    /// \mod m$.
72                    ///
73                    /// # Worst-case complexity
74                    /// $T(n) = O(n)$
75                    ///
76                    /// $M(n) = O(1)$
77                    ///
78                    /// where $T$ is time, $M$ is additional memory, and $n$ is
79                    /// `other.significant_bits()`.
80                    ///
81                    /// # Panics
82                    /// Panics if `self` is greater than or equal to `m`.
83                    ///
84                    /// # Examples
85                    /// See [here](super::mod_shr#mod_shr).
86                    #[inline]
87                    fn mod_shr(self, other: $u, m: $t) -> $t {
88                        mod_shr_signed(self, other, m)
89                    }
90                }
91
92                impl ModShrAssign<$u, $t> for $t {
93                    /// Right-shifts a number (divides it by a power of 2) modulo a number $m$, in
94                    /// place. The number must be already reduced modulo $m$.
95                    ///
96                    /// $x \gets y$, where $x, y < m$ and $\lfloor 2^{-n}x \rfloor \equiv y \mod m$.
97                    ///
98                    /// # Worst-case complexity
99                    /// $T(n) = O(n)$
100                    ///
101                    /// $M(n) = O(1)$
102                    ///
103                    /// where $T$ is time, $M$ is additional memory, and $n$ is
104                    /// `other.significant_bits()`.
105                    ///
106                    /// # Panics
107                    /// Panics if `self` is greater than or equal to `m`.
108                    ///
109                    /// # Examples
110                    /// See [here](super::mod_shr#mod_shr).
111                    #[inline]
112                    fn mod_shr_assign(&mut self, other: $u, m: $t) {
113                        mod_shr_assign_signed(self, other, m)
114                    }
115                }
116            };
117        }
118        apply_to_signeds!(impl_mod_shr_signed_inner);
119    };
120}
121apply_to_unsigneds!(impl_mod_shr_signed);