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
46forward_ubig_ibig_binop_to_repr!(impl Gcd, gcd, Output = UBig, impl_ibig_gcd);
47forward_ibig_ubig_binop_to_repr!(impl Gcd, gcd, Output = UBig, impl_ibig_gcd);
48forward_ubig_ibig_binop_to_repr!(impl ExtendedGcd, gcd_ext -> (UBig, IBig, IBig), OutputGcd = UBig, OutputCoeff = IBig, impl_ibig_gcd_ext);
49forward_ibig_ubig_binop_to_repr!(impl ExtendedGcd, gcd_ext -> (UBig, IBig, IBig), OutputGcd = UBig, OutputCoeff = IBig, impl_ibig_gcd_ext);
50
51mod repr {
52 use super::*;
53 use crate::{
54 add,
55 arch::word::{DoubleWord, Word},
56 buffer::Buffer,
57 cmp, div, gcd, memory,
58 memory::MemoryAllocation,
59 mul,
60 primitive::{shrink_dword, PrimitiveSigned},
61 repr::{
62 Repr,
63 TypedRepr::{self, *},
64 TypedReprRef::{self, *},
65 },
66 };
67 use core::cmp::Ordering;
68
69 impl<'l, 'r> Gcd<TypedReprRef<'r>> for TypedReprRef<'l> {
70 type Output = Repr;
71
72 fn gcd(self, rhs: TypedReprRef) -> Repr {
73 match (self, rhs) {
74 (RefSmall(dword0), RefSmall(dword1)) => Repr::from_dword(dword0.gcd(dword1)),
75 (RefSmall(dword0), RefLarge(words1)) => gcd_large_dword(words1, dword0),
76 (RefLarge(words0), RefSmall(dword1)) => gcd_large_dword(words0, dword1),
77 (RefLarge(words0), RefLarge(words1)) => gcd_large(words0.into(), words1.into()),
78 }
79 }
80 }
81
82 impl<'l> Gcd<TypedRepr> for TypedReprRef<'l> {
83 type Output = Repr;
84 #[inline]
85 fn gcd(self, rhs: TypedRepr) -> Self::Output {
86 self.gcd(rhs.as_ref())
87 }
88 }
89
90 impl<'r> Gcd<TypedReprRef<'r>> for TypedRepr {
91 type Output = Repr;
92 #[inline]
93 fn gcd(self, rhs: TypedReprRef) -> Self::Output {
94 self.as_ref().gcd(rhs)
95 }
96 }
97
98 impl Gcd<TypedRepr> for TypedRepr {
99 type Output = Repr;
100 #[inline]
101 fn gcd(self, rhs: TypedRepr) -> Self::Output {
102 self.as_ref().gcd(rhs.as_ref())
103 }
104 }
105
106 #[inline]
108 fn gcd_large_dword(buffer: &[Word], rhs: DoubleWord) -> Repr {
109 if rhs == 0 {
110 Repr::from_buffer(buffer.into())
111 } else if let Some(word) = shrink_dword(rhs) {
112 let rem = div::rem_by_word(buffer, word);
114 if rem == 0 {
115 Repr::from_word(word)
116 } else {
117 Repr::from_word(rem.gcd(word))
118 }
119 } else {
120 let rem = div::rem_by_dword(buffer, rhs);
122 if rem == 0 {
123 Repr::from_dword(rhs)
124 } else {
125 Repr::from_dword(rem.gcd(rhs))
126 }
127 }
128 }
129
130 #[inline]
132 fn gcd_large(mut lhs: Buffer, mut rhs: Buffer) -> Repr {
133 match cmp::cmp_in_place(&lhs, &rhs) {
135 Ordering::Greater => {}
136 Ordering::Equal => return Repr::from_buffer(lhs),
137 Ordering::Less => core::mem::swap(&mut lhs, &mut rhs),
138 };
139
140 let mut allocation =
141 MemoryAllocation::new(gcd::memory_requirement_exact(lhs.len(), rhs.len()));
142
143 let (len, swapped) = gcd::gcd_in_place(&mut lhs, &mut rhs, &mut allocation.memory());
144 if swapped {
145 rhs.truncate(len);
146 Repr::from_buffer(rhs)
147 } else {
148 lhs.truncate(len);
149 Repr::from_buffer(lhs)
150 }
151 }
152
153 impl<'l, 'r> ExtendedGcd<TypedReprRef<'r>> for TypedReprRef<'l> {
154 type OutputCoeff = Repr;
155 type OutputGcd = Repr;
156
157 fn gcd_ext(self, rhs: TypedReprRef<'r>) -> (Repr, Repr, Repr) {
158 match (self, rhs) {
159 (RefSmall(dword0), RefSmall(dword1)) => gcd_ext_dword(dword0, dword1),
160 (RefLarge(words0), RefSmall(dword1)) => gcd_ext_large_dword(words0.into(), dword1),
161 (RefSmall(dword0), RefLarge(words1)) => {
162 let (g, s, t) = gcd_ext_large_dword(words1.into(), dword0);
163 (g, t, s)
164 }
165 (RefLarge(words0), RefLarge(words1)) => gcd_ext_large(words0.into(), words1.into()),
166 }
167 }
168 }
169
170 impl<'r> ExtendedGcd<TypedReprRef<'r>> for TypedRepr {
171 type OutputCoeff = Repr;
172 type OutputGcd = Repr;
173
174 fn gcd_ext(self, rhs: TypedReprRef<'r>) -> (Repr, Repr, Repr) {
175 match (self, rhs) {
176 (Small(dword0), RefSmall(dword1)) => gcd_ext_dword(dword0, dword1),
177 (Large(buffer0), RefSmall(dword1)) => gcd_ext_large_dword(buffer0, dword1),
178 (Small(dword0), RefLarge(words1)) => {
179 let (g, s, t) = gcd_ext_large_dword(words1.into(), dword0);
180 (g, t, s)
181 }
182 (Large(buffer0), RefLarge(words1)) => gcd_ext_large(buffer0, words1.into()),
183 }
184 }
185 }
186
187 impl<'l> ExtendedGcd<TypedRepr> for TypedReprRef<'l> {
188 type OutputCoeff = Repr;
189 type OutputGcd = Repr;
190
191 fn gcd_ext(self, rhs: TypedRepr) -> (Repr, Repr, Repr) {
192 match (self, rhs) {
193 (RefSmall(dword0), Small(dword1)) => gcd_ext_dword(dword0, dword1),
194 (RefLarge(words0), Small(dword1)) => gcd_ext_large_dword(words0.into(), dword1),
195 (RefSmall(dword0), Large(buffer1)) => {
196 let (g, s, t) = gcd_ext_large_dword(buffer1, dword0);
197 (g, t, s)
198 }
199 (RefLarge(words0), Large(buffer1)) => gcd_ext_large(words0.into(), buffer1),
200 }
201 }
202 }
203
204 impl ExtendedGcd<TypedRepr> for TypedRepr {
205 type OutputCoeff = Repr;
206 type OutputGcd = Repr;
207
208 #[inline]
209 fn gcd_ext(self, rhs: TypedRepr) -> (Repr, Repr, Repr) {
210 match (self, rhs) {
211 (Small(dword0), Small(dword1)) => gcd_ext_dword(dword0, dword1),
212 (Large(buffer0), Small(dword1)) => gcd_ext_large_dword(buffer0, dword1),
213 (Small(dword0), Large(buffer1)) => {
214 let (g, s, t) = gcd_ext_large_dword(buffer1, dword0);
215 (g, t, s)
216 }
217 (Large(buffer0), Large(buffer1)) => gcd_ext_large(buffer0, buffer1),
218 }
219 }
220 }
221
222 #[inline]
223 fn gcd_ext_dword(lhs: DoubleWord, rhs: DoubleWord) -> (Repr, Repr, Repr) {
224 let (g, s, t) = lhs.gcd_ext(rhs);
225 let (s_sign, s_mag) = s.to_sign_magnitude();
226 let (t_sign, t_mag) = t.to_sign_magnitude();
227 (
228 Repr::from_dword(g),
229 Repr::from_dword(s_mag).with_sign(s_sign),
230 Repr::from_dword(t_mag).with_sign(t_sign),
231 )
232 }
233
234 #[inline]
236 fn gcd_ext_large_dword(mut buffer: Buffer, rhs: DoubleWord) -> (Repr, Repr, Repr) {
237 if rhs == 0 {
238 (Repr::from_buffer(buffer), Repr::one(), Repr::zero())
239 } else if let Some(word) = shrink_dword(rhs) {
240 let (g, a, b_sign) = gcd::gcd_ext_word(&mut buffer, word);
242 let (a_sign, a_mag) = a.to_sign_magnitude();
243 (
244 Repr::from_word(g),
245 Repr::from_word(a_mag).with_sign(a_sign),
246 Repr::from_buffer(buffer).with_sign(b_sign),
247 )
248 } else {
249 let (g, a, b_sign) = gcd::gcd_ext_dword(&mut buffer, rhs);
250 let (a_sign, a_mag) = a.to_sign_magnitude();
251 (
252 Repr::from_dword(g),
253 Repr::from_dword(a_mag).with_sign(a_sign),
254 Repr::from_buffer(buffer).with_sign(b_sign),
255 )
256 }
257 }
258
259 #[inline]
261 fn gcd_ext_large(mut lhs: Buffer, mut rhs: Buffer) -> (Repr, Repr, Repr) {
262 let swapped = match cmp::cmp_in_place(&lhs, &rhs) {
264 Ordering::Greater => false,
265 Ordering::Equal => return (Repr::from_buffer(lhs), Repr::one(), Repr::zero()),
266 Ordering::Less => {
267 core::mem::swap(&mut lhs, &mut rhs);
268 true
269 }
270 };
271 let (lhs_len, rhs_len) = (lhs.len(), rhs.len());
272
273 let clone_mem = memory::array_layout::<Word>(lhs_len + rhs_len);
275 let gcd_mem = gcd::memory_requirement_ext_exact(lhs_len, rhs_len);
276 let post_mem = memory::add_layout(
277 memory::array_layout::<Word>(lhs_len + rhs_len),
279 memory::max_layout(
280 mul::memory_requirement_exact(lhs_len + rhs_len, rhs_len),
282 div::memory_requirement_exact(lhs_len + rhs_len + 1, rhs_len),
283 ),
284 );
285 let mut allocation = MemoryAllocation::new(memory::add_layout(
286 clone_mem,
287 memory::max_layout(gcd_mem, post_mem),
288 ));
289 let mut memory = allocation.memory();
290
291 let (lhs_clone, mut memory) = memory.allocate_slice_copy(&lhs);
293 let (rhs_clone, mut memory) = memory.allocate_slice_copy(&rhs);
294
295 let (g_len, b_len, b_sign) = gcd::gcd_ext_in_place(&mut lhs, &mut rhs, &mut memory);
297
298 rhs.truncate(g_len);
301 let g = rhs;
302 lhs.truncate(b_len);
303 let b = lhs;
304
305 let brhs_len = rhs_clone.len() + b.len();
307 let (residue, mut memory) = memory.allocate_slice_fill(brhs_len + 1, 0);
308 mul::multiply(&mut residue[..brhs_len], rhs_clone, &b, &mut memory);
309 match b_sign {
310 Sign::Negative => {
311 *residue.last_mut().unwrap() = add::add_in_place(residue, &g) as Word;
312 }
313 Sign::Positive => {
314 let overflow = add::sub_in_place(residue, &g);
315 debug_assert!(!overflow);
316 }
317 };
318
319 let (shift, fast_div_top) = div::normalize(lhs_clone);
321 let overflow =
322 div::div_rem_unshifted_in_place(residue, lhs_clone, shift, fast_div_top, &mut memory);
323 let mut a = Buffer::from(&residue[lhs_len..]);
324 debug_assert_eq!(residue[0], 0); if overflow > 0 {
326 a.push(overflow);
327 }
328
329 let g = Repr::from_buffer(g);
330 let a = Repr::from_buffer(a).with_sign(-b_sign);
331 let b = Repr::from_buffer(b).with_sign(b_sign);
332 if swapped {
333 (g, b, a)
334 } else {
335 (g, a, b)
336 }
337 }
338}