Skip to main content

malachite_base/num/arithmetic/
mod_div.rs

1// Copyright © 2026 Mikhail Hogrefe
2//
3// Uses code adopted from the FLINT Library.
4//
5//      Copyright © 2009, 2015 William Hart
6//
7//      Copyright © 2019 Daniel Schultz
8//
9// This file is part of Malachite.
10//
11// Malachite is free software: you can redistribute it and/or modify it under the terms of the GNU
12// Lesser General Public License (LGPL) as published by the Free Software Foundation; either version
13// 3 of the License, or (at your option) any later version. See <https://www.gnu.org/licenses/>.
14
15use crate::num::arithmetic::traits::ModDiv;
16use crate::num::basic::signeds::PrimitiveSigned;
17use crate::num::basic::unsigneds::PrimitiveUnsigned;
18use crate::num::conversion::traits::WrappingFrom;
19
20// Computes `(gcd(x, y), s)`, where `s < y` and `sx ≡ gcd(x, y) mod y`. `x` must be reduced mod
21// `y`.
22//
23// This is n_gcdinv from ulong_extras/gcdinv.c, FLINT 3.6.0, where the GCD is returned along with
24// the cofactor.
25crate_test_fn! {gcdinv<
26    U: WrappingFrom<S> + PrimitiveUnsigned,
27    S: PrimitiveSigned + WrappingFrom<U>,
28>(
29    x: U,
30    y: U,
31) -> (U, U) {
32    assert!(x < y, "x must be reduced mod y, but {x} >= {y}");
33    let mut v1 = S::ZERO;
34    let mut v2 = S::ONE;
35    let mut r = x;
36    let mut x = y;
37    let mut d;
38    let mut t2;
39    // y and x both have their highest bit set
40    if (x & r).get_highest_bit() {
41        d = x - r;
42        t2 = v2;
43        x = r;
44        v2 = v1 - v2;
45        v1 = t2;
46        r = d;
47    }
48    // second value has its second-highest bit set
49    while r.get_bit(U::WIDTH - 2) {
50        d = x - r;
51        r = if d < r {
52            // quot = 1
53            t2 = v2;
54            x = r;
55            v2 = v1 - v2;
56            v1 = t2;
57            d
58        } else if d < (r << 1) {
59            // quot = 2
60            x = r;
61            t2 = v2;
62            v2 = v1 - (v2 << 1);
63            v1 = t2;
64            d - x
65        } else {
66            // quot = 3
67            x = r;
68            t2 = v2;
69            v2 = v1 - S::wrapping_from(3) * v2;
70            v1 = t2;
71            d - (x << 1)
72        };
73    }
74    while r != U::ZERO {
75        // overflow not possible, top 2 bits of r not set
76        r = if x < (r << 2) {
77            // quot < 4
78            d = x - r;
79            if d < r {
80                // quot = 1
81                t2 = v2;
82                x = r;
83                v2 = v1 - v2;
84                v1 = t2;
85                d
86            } else if d < (r << 1) {
87                // quot = 2
88                x = r;
89                t2 = v2;
90                v2 = v1.wrapping_sub(v2 << 1);
91                v1 = t2;
92                d - x
93            } else {
94                // quot = 3
95                x = r;
96                t2 = v2;
97                v2 = v1.wrapping_sub(S::wrapping_from(3).wrapping_mul(v2));
98                v1 = t2;
99                d.wrapping_sub(x << 1)
100            }
101        } else {
102            let (quot, rem) = x.div_rem(r);
103            x = r;
104            t2 = v2;
105            v2 = v1.wrapping_sub(S::wrapping_from(quot).wrapping_mul(v2));
106            v1 = t2;
107            rem
108        };
109    }
110    let mut s = U::wrapping_from(v1);
111    if v1 < S::ZERO {
112        s.wrapping_add_assign(y);
113    }
114    (x, s)
115}}
116
117// Computes a quotient of `b` and `c` modulo `m`: a `q` such that `qc ≡ b mod m`. `b` and `c` must
118// be reduced mod `m`.
119//
120// This is fmpz_mod_divides from fmpz_mod/divides.c, FLINT 3.6.0, where b and c are word-sized and
121// reduced mod the modulus, and the quotient is returned as an Option.
122private_test_fn! {mod_div_unsigned<
123    U: WrappingFrom<S> + PrimitiveUnsigned,
124    S: PrimitiveSigned + WrappingFrom<U>,
125>(
126    b: U,
127    c: U,
128    m: U,
129) -> Option<U> {
130    assert!(b < m, "b must be reduced mod m, but {b} >= {m}");
131    assert!(c < m, "c must be reduced mod m, but {c} >= {m}");
132    if c == U::ZERO {
133        return if b == U::ZERO {
134            Some(U::ZERO)
135        } else {
136            None
137        };
138    }
139    if b == U::ZERO {
140        return Some(U::ZERO);
141    }
142    // b and c are both nonzero now, so m >= 2. Solve g = cx + my, where g = gcd(c, m).
143    let (g, x) = gcdinv::<U, S>(c, m);
144    let (q, r) = b.div_rem(g);
145    if r == U::ZERO {
146        Some(q.mod_mul(x, m))
147    } else {
148        None
149    }
150}}
151
152macro_rules! impl_mod_div {
153    ($u:ident, $s:ident) => {
154        impl ModDiv<$u> for $u {
155            type Output = $u;
156
157            /// Divides a number by another number modulo a third number $m$, returning `None` if no
158            /// quotient exists. The inputs must be already reduced modulo $m$.
159            ///
160            /// A quotient exists if and only if $\gcd(y, m)$ divides $x$. If $y$ is not invertible
161            /// modulo $m$, the quotient is not unique; all quotients differ by multiples of
162            /// $m/\gcd(y, m)$, and this function returns one of them.
163            ///
164            /// $f(x, y, m) = \operatorname{Some}(q)$, where $x, y, q < m$ and $qy \equiv x \mod m$,
165            /// if such a $q$ exists.
166            ///
167            /// # Worst-case complexity
168            /// $T(n) = O(n)$
169            ///
170            /// $M(n) = O(1)$
171            ///
172            /// where $T$ is time, $M$ is additional memory, and $n$ is `m.significant_bits()`: the
173            /// extended Euclidean algorithm on words performs $O(n)$ iterations of constant-cost
174            /// word operations, with no allocation.
175            ///
176            /// # Panics
177            /// Panics if `self` or `other` are greater than or equal to `m`.
178            ///
179            /// # Examples
180            /// See [here](super::mod_div#mod_div).
181            #[inline]
182            fn mod_div(self, other: $u, m: $u) -> Option<$u> {
183                mod_div_unsigned::<$u, $s>(self, other, m)
184            }
185        }
186    };
187}
188apply_to_unsigned_signed_pairs!(impl_mod_div);