1use 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 #[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 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 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 #[inline]
156 fn gcd_large(mut lhs: Buffer, mut rhs: Buffer) -> Repr {
157 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 #[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 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 #[inline]
285 fn gcd_ext_large(mut lhs: Buffer, mut rhs: Buffer) -> (Repr, Repr, Repr) {
286 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 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 memory::array_layout::<Word>(lhs_len + rhs_len),
303 memory::max_layout(
304 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 let (lhs_clone, mut memory) = memory.allocate_slice_copy(&lhs);
317 let (rhs_clone, mut memory) = memory.allocate_slice_copy(&rhs);
318
319 let (g_len, b_len, b_sign) = gcd::gcd_ext_in_place(&mut lhs, &mut rhs, &mut memory);
321
322 rhs.truncate(g_len);
325 let g = rhs;
326 lhs.truncate(b_len);
327 let b = lhs;
328
329 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 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); 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}