dashu_int/
gcd_ops.rs

1//! Operators for finding greatest common divisor.
2
3use crate::{
4    helper_macros::{
5        forward_ibig_binop_to_repr, forward_ibig_ubig_binop_to_repr, forward_ubig_binop_to_repr,
6        forward_ubig_ibig_binop_to_repr,
7    },
8    ibig::IBig,
9    ubig::UBig,
10    Sign,
11};
12use dashu_base::ring::{ExtendedGcd, Gcd};
13
14forward_ubig_binop_to_repr!(impl Gcd, gcd);
15macro_rules! impl_ubig_gcd_ext {
16    ($repr0:ident, $repr1:ident) => {{
17        let (r, s, t) = $repr0.gcd_ext($repr1);
18        (UBig(r), IBig(s), IBig(t))
19    }};
20}
21forward_ubig_binop_to_repr!(
22    impl ExtendedGcd, gcd_ext -> (UBig, IBig, IBig),
23    OutputGcd = UBig, OutputCoeff = IBig,
24    impl_ubig_gcd_ext
25);
26
27macro_rules! impl_ibig_gcd {
28    ($sign0:ident, $mag0:ident, $sign1:ident, $mag1:ident) => {{
29        let _unused = ($sign0, $sign1);
30        UBig($mag0.gcd($mag1))
31    }};
32}
33macro_rules! impl_ibig_gcd_ext {
34    ($sign0:ident, $mag0:ident, $sign1:ident, $mag1:ident) => {{
35        let (r, s, t) = $mag0.gcd_ext($mag1);
36        (UBig(r), $sign0 * IBig(s), $sign1 * IBig(t))
37    }};
38}
39forward_ibig_binop_to_repr!(impl Gcd, gcd, Output = UBig, impl_ibig_gcd);
40forward_ibig_binop_to_repr!(
41    impl ExtendedGcd, gcd_ext -> (UBig, IBig, IBig),
42    OutputGcd = UBig, OutputCoeff = IBig,
43    impl_ibig_gcd_ext
44);
45
46macro_rules! impl_ubig_ibig_gcd {
47    ($mag0:ident, $sign1:ident, $mag1:ident) => {{
48        let _unused = $sign1;
49        UBig($mag0.gcd($mag1))
50    }};
51}
52macro_rules! impl_ibig_ubig_gcd {
53    ($sign0:ident, $mag0:ident, $mag1:ident) => {{
54        let _unused = $sign0;
55        UBig($mag0.gcd($mag1))
56    }};
57}
58macro_rules! impl_ubig_ibig_gcd_ext {
59    ($mag0:ident, $sign1:ident, $mag1:ident) => {{
60        let (r, s, t) = $mag0.gcd_ext($mag1);
61        (UBig(r), IBig(s), $sign1 * IBig(t))
62    }};
63}
64macro_rules! impl_ibig_ubig_gcd_ext {
65    ($sign0:ident, $mag0:ident, $mag1:ident) => {{
66        let (r, s, t) = $mag0.gcd_ext($mag1);
67        (UBig(r), $sign0 * IBig(s), IBig(t))
68    }};
69}
70forward_ubig_ibig_binop_to_repr!(impl Gcd, gcd, Output = UBig, impl_ubig_ibig_gcd);
71forward_ibig_ubig_binop_to_repr!(impl Gcd, gcd, Output = UBig, impl_ibig_ubig_gcd);
72forward_ubig_ibig_binop_to_repr!(impl ExtendedGcd, gcd_ext -> (UBig, IBig, IBig), OutputGcd = UBig, OutputCoeff = IBig, impl_ubig_ibig_gcd_ext);
73forward_ibig_ubig_binop_to_repr!(impl ExtendedGcd, gcd_ext -> (UBig, IBig, IBig), OutputGcd = UBig, OutputCoeff = IBig, impl_ibig_ubig_gcd_ext);
74
75mod repr {
76    use super::*;
77    use crate::{
78        add,
79        arch::word::{DoubleWord, Word},
80        buffer::Buffer,
81        cmp, div, gcd, memory,
82        memory::MemoryAllocation,
83        mul,
84        primitive::{shrink_dword, PrimitiveSigned},
85        repr::{
86            Repr,
87            TypedRepr::{self, *},
88            TypedReprRef::{self, *},
89        },
90    };
91    use core::cmp::Ordering;
92
93    impl<'l, 'r> Gcd<TypedReprRef<'r>> for TypedReprRef<'l> {
94        type Output = Repr;
95
96        fn gcd(self, rhs: TypedReprRef) -> Repr {
97            match (self, rhs) {
98                (RefSmall(dword0), RefSmall(dword1)) => Repr::from_dword(dword0.gcd(dword1)),
99                (RefSmall(dword0), RefLarge(words1)) => gcd_large_dword(words1, dword0),
100                (RefLarge(words0), RefSmall(dword1)) => gcd_large_dword(words0, dword1),
101                (RefLarge(words0), RefLarge(words1)) => gcd_large(words0.into(), words1.into()),
102            }
103        }
104    }
105
106    impl<'l> Gcd<TypedRepr> for TypedReprRef<'l> {
107        type Output = Repr;
108        #[inline]
109        fn gcd(self, rhs: TypedRepr) -> Self::Output {
110            self.gcd(rhs.as_ref())
111        }
112    }
113
114    impl<'r> Gcd<TypedReprRef<'r>> for TypedRepr {
115        type Output = Repr;
116        #[inline]
117        fn gcd(self, rhs: TypedReprRef) -> Self::Output {
118            self.as_ref().gcd(rhs)
119        }
120    }
121
122    impl Gcd<TypedRepr> for TypedRepr {
123        type Output = Repr;
124        #[inline]
125        fn gcd(self, rhs: TypedRepr) -> Self::Output {
126            self.as_ref().gcd(rhs.as_ref())
127        }
128    }
129
130    /// Perform gcd on a large number with a `DoubleWord`.
131    #[inline]
132    fn gcd_large_dword(buffer: &[Word], rhs: DoubleWord) -> Repr {
133        if rhs == 0 {
134            Repr::from_buffer(buffer.into())
135        } else if let Some(word) = shrink_dword(rhs) {
136            // reduce the large number by single word rhs
137            let rem = div::rem_by_word(buffer, word);
138            if rem == 0 {
139                Repr::from_word(word)
140            } else {
141                Repr::from_word(rem.gcd(word))
142            }
143        } else {
144            // reduce the large number by double word rhs
145            let rem = div::rem_by_dword(buffer, rhs);
146            if rem == 0 {
147                Repr::from_dword(rhs)
148            } else {
149                Repr::from_dword(rem.gcd(rhs))
150            }
151        }
152    }
153
154    /// Perform gcd on two large numbers.
155    #[inline]
156    fn gcd_large(mut lhs: Buffer, mut rhs: Buffer) -> Repr {
157        // make sure lhs > rhs
158        match cmp::cmp_in_place(&lhs, &rhs) {
159            Ordering::Greater => {}
160            Ordering::Equal => return Repr::from_buffer(lhs),
161            Ordering::Less => core::mem::swap(&mut lhs, &mut rhs),
162        };
163
164        let mut allocation =
165            MemoryAllocation::new(gcd::memory_requirement_exact(lhs.len(), rhs.len()));
166
167        let (len, swapped) = gcd::gcd_in_place(&mut lhs, &mut rhs, &mut allocation.memory());
168        if swapped {
169            rhs.truncate(len);
170            Repr::from_buffer(rhs)
171        } else {
172            lhs.truncate(len);
173            Repr::from_buffer(lhs)
174        }
175    }
176
177    impl<'l, 'r> ExtendedGcd<TypedReprRef<'r>> for TypedReprRef<'l> {
178        type OutputCoeff = Repr;
179        type OutputGcd = Repr;
180
181        fn gcd_ext(self, rhs: TypedReprRef<'r>) -> (Repr, Repr, Repr) {
182            match (self, rhs) {
183                (RefSmall(dword0), RefSmall(dword1)) => gcd_ext_dword(dword0, dword1),
184                (RefLarge(words0), RefSmall(dword1)) => gcd_ext_large_dword(words0.into(), dword1),
185                (RefSmall(dword0), RefLarge(words1)) => {
186                    let (g, s, t) = gcd_ext_large_dword(words1.into(), dword0);
187                    (g, t, s)
188                }
189                (RefLarge(words0), RefLarge(words1)) => gcd_ext_large(words0.into(), words1.into()),
190            }
191        }
192    }
193
194    impl<'r> ExtendedGcd<TypedReprRef<'r>> for TypedRepr {
195        type OutputCoeff = Repr;
196        type OutputGcd = Repr;
197
198        fn gcd_ext(self, rhs: TypedReprRef<'r>) -> (Repr, Repr, Repr) {
199            match (self, rhs) {
200                (Small(dword0), RefSmall(dword1)) => gcd_ext_dword(dword0, dword1),
201                (Large(buffer0), RefSmall(dword1)) => gcd_ext_large_dword(buffer0, dword1),
202                (Small(dword0), RefLarge(words1)) => {
203                    let (g, s, t) = gcd_ext_large_dword(words1.into(), dword0);
204                    (g, t, s)
205                }
206                (Large(buffer0), RefLarge(words1)) => gcd_ext_large(buffer0, words1.into()),
207            }
208        }
209    }
210
211    impl<'l> ExtendedGcd<TypedRepr> for TypedReprRef<'l> {
212        type OutputCoeff = Repr;
213        type OutputGcd = Repr;
214
215        fn gcd_ext(self, rhs: TypedRepr) -> (Repr, Repr, Repr) {
216            match (self, rhs) {
217                (RefSmall(dword0), Small(dword1)) => gcd_ext_dword(dword0, dword1),
218                (RefLarge(words0), Small(dword1)) => gcd_ext_large_dword(words0.into(), dword1),
219                (RefSmall(dword0), Large(buffer1)) => {
220                    let (g, s, t) = gcd_ext_large_dword(buffer1, dword0);
221                    (g, t, s)
222                }
223                (RefLarge(words0), Large(buffer1)) => gcd_ext_large(words0.into(), buffer1),
224            }
225        }
226    }
227
228    impl ExtendedGcd<TypedRepr> for TypedRepr {
229        type OutputCoeff = Repr;
230        type OutputGcd = Repr;
231
232        #[inline]
233        fn gcd_ext(self, rhs: TypedRepr) -> (Repr, Repr, Repr) {
234            match (self, rhs) {
235                (Small(dword0), Small(dword1)) => gcd_ext_dword(dword0, dword1),
236                (Large(buffer0), Small(dword1)) => gcd_ext_large_dword(buffer0, dword1),
237                (Small(dword0), Large(buffer1)) => {
238                    let (g, s, t) = gcd_ext_large_dword(buffer1, dword0);
239                    (g, t, s)
240                }
241                (Large(buffer0), Large(buffer1)) => gcd_ext_large(buffer0, buffer1),
242            }
243        }
244    }
245
246    #[inline]
247    fn gcd_ext_dword(lhs: DoubleWord, rhs: DoubleWord) -> (Repr, Repr, Repr) {
248        let (g, s, t) = lhs.gcd_ext(rhs);
249        let (s_sign, s_mag) = s.to_sign_magnitude();
250        let (t_sign, t_mag) = t.to_sign_magnitude();
251        (
252            Repr::from_dword(g),
253            Repr::from_dword(s_mag).with_sign(s_sign),
254            Repr::from_dword(t_mag).with_sign(t_sign),
255        )
256    }
257
258    /// Perform extended gcd on a large number with a `Word`.
259    #[inline]
260    fn gcd_ext_large_dword(mut buffer: Buffer, rhs: DoubleWord) -> (Repr, Repr, Repr) {
261        if rhs == 0 {
262            (Repr::from_buffer(buffer), Repr::one(), Repr::zero())
263        } else if let Some(word) = shrink_dword(rhs) {
264            // reduce the large number by single word rhs
265            let (g, a, b_sign) = gcd::gcd_ext_word(&mut buffer, word);
266            let (a_sign, a_mag) = a.to_sign_magnitude();
267            (
268                Repr::from_word(g),
269                Repr::from_word(a_mag).with_sign(a_sign),
270                Repr::from_buffer(buffer).with_sign(b_sign),
271            )
272        } else {
273            let (g, a, b_sign) = gcd::gcd_ext_dword(&mut buffer, rhs);
274            let (a_sign, a_mag) = a.to_sign_magnitude();
275            (
276                Repr::from_dword(g),
277                Repr::from_dword(a_mag).with_sign(a_sign),
278                Repr::from_buffer(buffer).with_sign(b_sign),
279            )
280        }
281    }
282
283    /// Perform extended gcd on two large numbers.
284    #[inline]
285    fn gcd_ext_large(mut lhs: Buffer, mut rhs: Buffer) -> (Repr, Repr, Repr) {
286        // make sure lhs > rhs
287        let swapped = match cmp::cmp_in_place(&lhs, &rhs) {
288            Ordering::Greater => false,
289            Ordering::Equal => return (Repr::from_buffer(lhs), Repr::one(), Repr::zero()),
290            Ordering::Less => {
291                core::mem::swap(&mut lhs, &mut rhs);
292                true
293            }
294        };
295        let (lhs_len, rhs_len) = (lhs.len(), rhs.len());
296
297        // allocate memory
298        let clone_mem = memory::array_layout::<Word>(lhs_len + rhs_len);
299        let gcd_mem = gcd::memory_requirement_ext_exact(lhs_len, rhs_len);
300        let post_mem = memory::add_layout(
301            // temporary space to store residue
302            memory::array_layout::<Word>(lhs_len + rhs_len),
303            memory::max_layout(
304                // memory required for post processing: one multiplication + one division
305                mul::memory_requirement_exact(lhs_len + rhs_len, rhs_len),
306                div::memory_requirement_exact(lhs_len + rhs_len + 1, rhs_len),
307            ),
308        );
309        let mut allocation = MemoryAllocation::new(memory::add_layout(
310            clone_mem,
311            memory::max_layout(gcd_mem, post_mem),
312        ));
313        let mut memory = allocation.memory();
314
315        // copy oprands for post processing
316        let (lhs_clone, mut memory) = memory.allocate_slice_copy(&lhs);
317        let (rhs_clone, mut memory) = memory.allocate_slice_copy(&rhs);
318
319        // actual computation
320        let (g_len, b_len, b_sign) = gcd::gcd_ext_in_place(&mut lhs, &mut rhs, &mut memory);
321
322        // the result from the internal function is g = gcd(lhs, rhs), b s.t g = b*rhs mod lhs
323        // post processing: a = (g - rhs * b) / lhs
324        rhs.truncate(g_len);
325        let g = rhs;
326        lhs.truncate(b_len);
327        let b = lhs;
328
329        // residue = g - rhs * b
330        let brhs_len = rhs_clone.len() + b.len();
331        let (residue, mut memory) = memory.allocate_slice_fill(brhs_len + 1, 0);
332        mul::multiply(&mut residue[..brhs_len], rhs_clone, &b, &mut memory);
333        match b_sign {
334            Sign::Negative => {
335                *residue.last_mut().unwrap() = add::add_in_place(residue, &g) as Word;
336            }
337            Sign::Positive => {
338                let overflow = add::sub_in_place(residue, &g);
339                debug_assert!(!overflow);
340            }
341        };
342
343        // a = residue / lhs
344        let (shift, fast_div_top) = div::normalize(lhs_clone);
345        let overflow =
346            div::div_rem_unshifted_in_place(residue, lhs_clone, shift, fast_div_top, &mut memory);
347        let mut a = Buffer::from(&residue[lhs_len..]);
348        debug_assert_eq!(residue[0], 0); // this division is an exact division
349        if overflow > 0 {
350            a.push(overflow);
351        }
352
353        let g = Repr::from_buffer(g);
354        let a = Repr::from_buffer(a).with_sign(-b_sign);
355        let b = Repr::from_buffer(b).with_sign(b_sign);
356        if swapped {
357            (g, b, a)
358        } else {
359            (g, a, b)
360        }
361    }
362}