use crate::integer::Integer;
use crate::natural::InnerNatural::Small;
use crate::natural::Natural;
use crate::natural::arithmetic::add::{
limbs_add_to_out_aliased, limbs_slice_add_greater_in_place_left,
limbs_slice_add_same_length_in_place_left,
};
use crate::natural::arithmetic::add_mul::limbs_slice_add_mul_limb_same_length_in_place_left;
use crate::natural::arithmetic::div_exact::limbs_div_exact_to_out;
use crate::natural::arithmetic::div_mod::{
limbs_div_limb_to_out_mod, limbs_div_mod_qs_to_out_rs_to_ns,
};
use crate::natural::arithmetic::gcd::half_gcd::{
GcdSubdivideStepContext, HalfGcdMatrix, HalfGcdMatrix1, extract_number,
limbs_gcd_subdivide_step, limbs_half_gcd, limbs_half_gcd_2,
limbs_half_gcd_matrix_1_mul_inverse_vector, limbs_half_gcd_matrix_1_mul_vector,
limbs_half_gcd_matrix_adjust, limbs_half_gcd_matrix_init_scratch_len,
limbs_half_gcd_scratch_len,
};
use crate::natural::arithmetic::mul::limb::limbs_mul_limb_to_out;
use crate::natural::arithmetic::mul::{limbs_mul_to_out, limbs_mul_to_out_scratch_len};
use crate::natural::arithmetic::sub::limbs_sub_greater_in_place_left;
use crate::natural::comparison::cmp::limbs_cmp_same_length;
use crate::platform::{DoubleLimb, Limb};
use core::cmp::{Ordering::*, max};
use core::mem::swap;
use malachite_base::fail_on_untested_path;
use malachite_base::num::arithmetic::traits::{
DivExact, ExtendedGcd, NegAssign, OverflowingAddAssign,
};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::{One, Zero};
use malachite_base::num::conversion::traits::ExactFrom;
use malachite_base::slices::{slice_set_zero, slice_test_zero, slice_trailing_zeros};
struct ExtendedGcdContext<'a> {
gs: &'a mut [Limb],
gs_len: usize,
ss: &'a mut [Limb],
ss_len: usize,
ss_sign: bool,
us_len: usize,
us0: &'a mut [Limb],
us1: &'a mut [Limb],
scratch: &'a mut [Limb],
}
impl<'a> ExtendedGcdContext<'a> {
const fn new(
gs: &'a mut [Limb],
ss: &'a mut [Limb],
us_len: usize,
us0: &'a mut [Limb],
us1: &'a mut [Limb],
scratch: &'a mut [Limb],
) -> Self {
let gs_len = gs.len();
Self {
gs,
gs_len,
ss,
ss_len: 0,
ss_sign: false,
us_len,
us0,
us1,
scratch,
}
}
}
impl GcdSubdivideStepContext for ExtendedGcdContext<'_> {
fn gcd_subdiv_step_hook(
&mut self,
gs: Option<&[Limb]>,
qs: Option<&mut [Limb]>,
mut qs_len: usize,
mut d: i8,
) {
let mut us_len = self.us_len;
if let Some(gs) = gs {
let gs_len = gs.len();
assert_ne!(gs_len, 0);
assert!(gs[gs_len - 1] > 0);
self.gs_len = gs_len;
self.gs[..gs_len].copy_from_slice(gs);
if d == -1 {
let c = limbs_cmp_same_length(&self.us0[..us_len], &self.us1[..us_len]);
assert!(c != Equal || us_len == 1 && self.us0[0] == 1 && self.us1[0] == 1);
d = i8::from(c == Less);
}
let ss = if d == 0 {
&mut *self.us1
} else {
&mut *self.us0
};
us_len -= slice_trailing_zeros(&ss[..us_len]);
self.ss[..us_len].copy_from_slice(&ss[..us_len]);
self.us_len = us_len;
self.ss_len = us_len;
self.ss_sign = d == 0;
} else {
let mut us0 = &mut *self.us0;
let mut us1 = &mut *self.us1;
if d != 0 {
swap(&mut us0, &mut us1);
}
let qs = qs.as_ref().unwrap();
if qs[qs_len - 1] == 0 {
qs_len -= 1;
}
let carry = if qs_len == 1 {
let q = qs[0];
let us0 = &mut us0[..us_len];
let us1 = &us1[..us_len];
if q == 1 {
Limb::from(limbs_slice_add_same_length_in_place_left(us0, us1))
} else {
limbs_slice_add_mul_limb_same_length_in_place_left(us0, us1, q)
}
} else {
let mut us1_len = us_len;
us1_len -= slice_trailing_zeros(&us1[..us1_len]);
if us1_len == 0 {
return;
}
let scratch = &mut *self.scratch;
let mut mul_scratch = vec![0; limbs_mul_to_out_scratch_len(qs_len, us1_len)];
limbs_mul_to_out(scratch, &qs[..qs_len], &us1[..us1_len], &mut mul_scratch);
us1_len += qs_len;
if scratch[us1_len - 1] == 0 {
us1_len -= 1;
}
let us0 = &mut us0[..us1_len];
let scratch = &scratch[..us1_len];
Limb::from(if us1_len >= us_len {
let us_len_old = us_len;
us_len = us1_len;
limbs_add_to_out_aliased(us0, us_len_old, scratch)
} else {
fail_on_untested_path("gcd_subdiv_step_hook, us1_len < us_len");
limbs_slice_add_greater_in_place_left(us0, scratch)
})
};
us0[us_len] = carry;
self.us_len = us_len;
if carry > 0 {
self.us_len += 1;
}
}
}
}
fn limbs_extended_gcd_same_length_lehmer<'a>(
gs: &mut [Limb],
ss: &mut [Limb],
mut xs: &'a mut [Limb],
ys: &mut [Limb],
scratch: &'a mut [Limb],
) -> (usize, usize, bool) {
let mut n = xs.len();
assert_eq!(ys.len(), n);
let scratch_len = n + 1;
let (us, mut scratch) = scratch.split_at_mut(3 * scratch_len);
slice_set_zero(us);
let (mut us0, remainder) = us.split_at_mut(scratch_len);
let (us1, mut u2) = remainder.split_at_mut(scratch_len);
us1[0] = 1;
let mut us_len = 1;
while n >= 2 {
let mut m = HalfGcdMatrix1::default();
let mask = xs[n - 1] | ys[n - 1];
assert_ne!(mask, 0);
let (ah, al, bh, bl) = if mask.get_highest_bit() {
(xs[n - 1], xs[n - 2], ys[n - 1], ys[n - 2])
} else if n == 2 {
let shift = u64::from(mask.leading_zeros());
(
extract_number(shift, xs[1], xs[0]),
xs[0] << shift,
extract_number(shift, ys[1], ys[0]),
ys[0] << shift,
)
} else {
let shift = u64::from(mask.leading_zeros());
(
extract_number(shift, xs[n - 1], xs[n - 2]),
extract_number(shift, xs[n - 2], xs[n - 3]),
extract_number(shift, ys[n - 1], ys[n - 2]),
extract_number(shift, ys[n - 2], ys[n - 3]),
)
};
if limbs_half_gcd_2(ah, al, bh, bl, &mut m) {
n = limbs_half_gcd_matrix_1_mul_inverse_vector(
&m,
&mut scratch[..n],
&xs[..n],
&mut ys[..n],
);
swap(&mut xs, &mut scratch);
us_len = limbs_half_gcd_matrix_1_mul_vector(&m, u2, &us0[..us_len], us1);
swap(&mut us0, &mut u2);
} else {
let mut context = ExtendedGcdContext::new(gs, ss, us_len, us0, us1, u2);
n = limbs_gcd_subdivide_step(&mut xs[..n], &mut ys[..n], 0, &mut context, scratch);
if n == 0 {
return (context.gs_len, context.ss_len, context.ss_sign);
}
us_len = context.us_len;
}
}
assert_ne!(xs[0], 0);
assert_ne!(ys[0], 0);
let negate;
if xs[0] == ys[0] {
gs[0] = xs[0];
let c = limbs_cmp_same_length(&us0[..us_len], &us1[..us_len]);
assert!(c != Equal || us_len == 1 && us0[0] == 1 && us1[0] == 1);
let ss_sign = c != Less;
let u = if ss_sign { us1 } else { us0 };
us_len -= slice_trailing_zeros(&u[..us_len]);
ss[..us_len].copy_from_slice(&u[..us_len]);
(1, us_len, ss_sign)
} else {
let (g, mut u, mut v) = xs[0].extended_gcd(ys[0]);
gs[0] = g;
if u == 0 {
assert_eq!(v, 1);
us_len -= slice_trailing_zeros(&us0[..us_len]);
ss[..us_len].copy_from_slice(&us0[..us_len]);
return (1, us_len, false);
} else if v == 0 {
assert_eq!(u, 1);
us_len -= slice_trailing_zeros(&us1[..us_len]);
ss[..us_len].copy_from_slice(&us1[..us_len]);
return (1, us_len, true);
} else if u > 0 {
negate = false;
assert!(v < 0);
v.neg_assign();
} else {
negate = true;
assert!(v > 0);
u.neg_assign();
}
let mut u_high =
limbs_mul_limb_to_out::<DoubleLimb, Limb>(ss, &us1[..us_len], Limb::exact_from(u));
let v_high = limbs_slice_add_mul_limb_same_length_in_place_left(
&mut ss[..us_len],
&us0[..us_len],
Limb::exact_from(v),
);
if u_high != 0 || v_high != 0 {
let overflow = u_high.overflowing_add_assign(v_high);
ss[us_len] = u_high;
us_len += 1;
if overflow {
fail_on_untested_path("limbs_extended_gcd_same_length_lehmer, overflow");
ss[us_len] = 1;
us_len += 1;
}
}
us_len -= slice_trailing_zeros(&ss[..us_len]);
assert_ne!(us_len, 0);
(1, us_len, !negate)
}
}
fn limbs_half_gcd_matrix_mul_vector(
m: &mut HalfGcdMatrix<'_>,
rp: &mut [Limb],
xs: &[Limb],
ys: &mut [Limb],
scratch: &mut [Limb],
) -> usize {
let n = xs.len();
let ys_lo = &ys[..n];
let m_n = m.n;
let mut big_n = n + m_n;
let scratch = &mut scratch[..big_n];
let (m00, m01, m10, m11) = m.get_four();
let mut mul_scratch = vec![0; limbs_mul_to_out_scratch_len(m_n, n)];
limbs_mul_to_out(scratch, &m00[..m_n], xs, &mut mul_scratch);
limbs_mul_to_out(&mut rp[..big_n], &m10[..m_n], ys_lo, &mut mul_scratch);
let a_high = limbs_slice_add_same_length_in_place_left(&mut rp[..big_n], scratch);
limbs_mul_to_out(scratch, &m11[..m_n], ys_lo, &mut mul_scratch);
limbs_mul_to_out(ys, &m01[..m_n], xs, &mut mul_scratch);
let b_high = limbs_slice_add_same_length_in_place_left(&mut ys[..big_n], scratch);
if a_high || b_high {
rp[big_n] = Limb::from(a_high);
ys[big_n] = Limb::from(b_high);
big_n += 1;
} else {
while rp[big_n - 1] == 0 && ys[big_n - 1] == 0 {
big_n -= 1;
}
}
big_n
}
fn limbs_extended_gcd_cofactor(
vs: &mut [Limb],
xs: &[Limb],
ys: &mut [Limb],
gs: &[Limb],
ss: &[Limb],
ss_len: usize,
ss_sign: bool,
scratch: &mut [Limb],
) -> usize {
let n = xs.len();
assert_eq!(ys.len(), n);
let gs_len = gs.len();
assert_ne!(n, 0);
assert_ne!(gs_len, 0);
assert_ne!(ss_len, 0);
let mut size = ss_len;
assert!(size <= n);
assert_ne!(ss[size - 1], 0);
let xs_len = n - slice_trailing_zeros(xs);
assert!(gs_len <= xs_len);
let mut mul_scratch = vec![0; limbs_mul_to_out_scratch_len(xs_len, size)];
limbs_mul_to_out(scratch, &xs[..xs_len], &ss[..size], &mut mul_scratch);
size += xs_len;
let scratch = &mut scratch[..size];
if ss_len != 0 && ss_sign {
assert!(!limbs_sub_greater_in_place_left(scratch, gs));
size -= slice_trailing_zeros(scratch);
if size == 0 {
return 0;
}
} else {
assert!(!limbs_slice_add_greater_in_place_left(scratch, gs));
if scratch[size - 1] == 0 {
size -= 1;
}
}
let ys_len = n - slice_trailing_zeros(ys);
assert!(size >= ys_len);
let vs_len = size + 1 - ys_len;
assert!(vs_len <= n + 1);
limbs_div_exact_to_out(vs, &mut scratch[..size], &mut ys[..ys_len]);
if vs[vs_len - 1] == 0 {
vs_len - 1
} else {
vs_len
}
}
const fn choose_p_1(n: usize) -> usize {
n >> 1
}
const fn choose_p_2(n: usize) -> usize {
n / 3
}
const fn limbs_extended_gcd_same_length_lehmer_scratch_len(n: usize) -> usize {
(n << 2) + 3
}
const GCDEXT_DC_THRESHOLD: usize = 242;
pub fn limbs_extended_gcd(
gs: &mut [Limb],
ss: &mut [Limb],
xs: &mut [Limb],
ys: &mut [Limb],
) -> (usize, bool) {
let xs_len = xs.len();
let mut n = ys.len();
let scratch_len = n + 1;
assert!(xs_len >= n);
assert_ne!(n, 0);
assert_ne!(ys[n - 1], 0);
let scratch = xs_len - n + 1;
let mut scratch_2 = max(
scratch,
limbs_extended_gcd_same_length_lehmer_scratch_len(n),
);
let mut matrix_scratch = 0;
if n >= GCDEXT_DC_THRESHOLD {
let max_p = choose_p_1(n);
let min_p = choose_p_2(n);
matrix_scratch = limbs_half_gcd_matrix_init_scratch_len(n - min_p);
let hgcd_scratch = limbs_half_gcd_scratch_len(n - min_p);
let update_scratch = max_p + n - 1;
let mut scratch = matrix_scratch + max(hgcd_scratch, update_scratch);
scratch_2 = max(scratch_2, scratch);
scratch = limbs_extended_gcd_same_length_lehmer_scratch_len(GCDEXT_DC_THRESHOLD)
+ 3 * GCDEXT_DC_THRESHOLD;
scratch_2 = max(scratch_2, scratch);
scratch_2 += (n + 1) << 1;
}
let mut scratch = vec![0; scratch_2];
if xs_len > n {
if n == 1 {
xs[0] = limbs_div_limb_to_out_mod(&mut scratch, xs, ys[0]);
} else {
limbs_div_mod_qs_to_out_rs_to_ns(&mut scratch, xs, ys);
}
if slice_test_zero(&xs[..n]) {
gs[..n].copy_from_slice(ys);
return (n, true);
}
}
let xs_lo = &mut xs[..n];
if n < GCDEXT_DC_THRESHOLD {
let (gs_len, _, ss_sign) =
limbs_extended_gcd_same_length_lehmer(gs, ss, xs_lo, ys, &mut scratch);
return (gs_len, ss_sign);
}
slice_set_zero(&mut scratch[..scratch_len << 1]);
split_into_chunks_mut!(scratch, scratch_len, [us0, us1], scratch);
let p = choose_p_1(n);
let (scratch_lo, scratch_hi) = scratch.split_at_mut(matrix_scratch);
let mut m = HalfGcdMatrix::init(n - p, scratch_lo);
let nn = limbs_half_gcd(&mut xs_lo[p..], &mut ys[p..], &mut m, scratch_hi);
let mut ss_sign;
let mut us_len;
if nn != 0 {
assert!(m.n <= (n - p - 1) >> 1);
assert!(m.n + p <= (p + n - 1) >> 1);
n = limbs_half_gcd_matrix_adjust(&m, p + nn, xs, ys, p, scratch_hi);
us0[..m.n].copy_from_slice(&m.get(1, 0)[..m.n]);
us1[..m.n].copy_from_slice(&m.get(1, 1)[..m.n]);
us_len = m.n;
while us0[us_len - 1] == 0 && us1[us_len - 1] == 0 {
us_len -= 1;
}
} else {
us1[0] = 1;
let (scratch, scratch_hi) = scratch.split_at_mut(n);
let mut context = ExtendedGcdContext::new(gs, ss, 1, us0, us1, &mut scratch_hi[n..]);
n = limbs_gcd_subdivide_step(&mut xs[..n], ys, 0, &mut context, scratch);
ss_sign = context.ss_sign;
if n == 0 {
return (context.gs_len, ss_sign);
}
us_len = context.us_len;
assert!(us_len < scratch_len);
}
while n >= GCDEXT_DC_THRESHOLD {
let p = choose_p_2(n);
let (scratch_lo, scratch_hi) = scratch.split_at_mut(matrix_scratch);
let mut m = HalfGcdMatrix::init(n - p, scratch_lo);
let nn = limbs_half_gcd(&mut xs[p..n], &mut ys[p..n], &mut m, scratch_hi);
if nn != 0 {
let t0 = scratch_hi;
assert!(m.n <= (n - p - 1) >> 1);
assert!(m.n + p <= (p + n - 1) >> 1);
n = limbs_half_gcd_matrix_adjust(&m, p + nn, xs, ys, p, t0);
assert!(m.n + us_len <= scratch_len);
t0[..us_len].copy_from_slice(&us0[..us_len]);
let (t0_lo, t0_hi) = t0.split_at_mut(us_len);
us_len = limbs_half_gcd_matrix_mul_vector(&mut m, us0, t0_lo, us1, t0_hi);
assert!(us_len < scratch_len);
assert!(us0[us_len - 1] != 0 || us1[us_len - 1] != 0);
} else {
let (scratch_lo, scratch_hi) = scratch.split_at_mut(n);
let mut context = ExtendedGcdContext::new(gs, ss, us_len, us0, us1, scratch_hi);
n = limbs_gcd_subdivide_step(&mut xs[..n], &mut ys[..n], 0, &mut context, scratch_lo);
ss_sign = context.ss_sign;
if n == 0 {
return (context.gs_len, ss_sign);
}
us_len = context.us_len;
assert!(us_len < scratch_len);
}
}
let n = n;
let xs = &mut xs[..n];
let ys = &mut ys[..n];
assert!(*xs.last().unwrap() != 0 || *ys.last().unwrap() != 0);
if limbs_cmp_same_length(xs, ys) == Equal {
gs[..n].copy_from_slice(xs);
let c = limbs_cmp_same_length(&us0[..us_len], &us1[..us_len]);
assert!(c != Equal || us_len == 1 && us0[0] == 1 && us1[0] == 1);
if c == Less {
us_len -= slice_trailing_zeros(&us0[..us_len]);
ss[..us_len].copy_from_slice(&us0[..us_len]);
ss_sign = false;
} else {
us_len -= slice_trailing_zeros(&us1[..us_len]);
assert_ne!(us_len, 0);
ss[..us_len].copy_from_slice(&us1[..us_len]);
ss_sign = true;
}
(n, ss_sign)
} else if us0[0] == 0 && us_len == 1 {
fail_on_untested_path(
"limbs_extended_gcd, \
limbs_cmp_same_length(..) != Equal && us0[0] == 0 && us_len == 1",
);
assert_eq!(us1[0], 1);
let (gs_len, _, ss_sign) = limbs_extended_gcd_same_length_lehmer(gs, ss, xs, ys, scratch);
(gs_len, ss_sign)
} else {
let (lehmer_ss, scratch) = scratch.split_at_mut(n);
split_into_chunks_mut!(scratch, n, [scratch_0, scratch_1], scratch_2);
scratch_0.copy_from_slice(xs);
scratch_1.copy_from_slice(ys);
let (gs_len, lehmer_us_len, lehmer_us_sign) =
limbs_extended_gcd_same_length_lehmer(gs, lehmer_ss, scratch_0, scratch_1, scratch_2);
let us0_len = us_len - slice_trailing_zeros(&us0[..us_len]);
let us0 = &us0[..us0_len];
assert_ne!(us0_len, 0);
if lehmer_us_len == 0 {
ss[..us0_len].copy_from_slice(us0);
return (gs_len, false);
}
let lehmer_ss = &lehmer_ss[..lehmer_us_len];
let (lehmer_vs, scratch_hi) = scratch.split_at_mut(n + 1);
let lehmer_vs_len = limbs_extended_gcd_cofactor(
lehmer_vs,
xs,
ys,
&gs[..gs_len],
lehmer_ss,
lehmer_us_len,
lehmer_us_sign,
scratch_hi,
);
let mut us1_len = us_len - slice_trailing_zeros(&us1[..us_len]);
assert_ne!(us1_len, 0);
assert!(lehmer_vs_len + us0_len <= scratch_len);
let mut mul_scratch = vec![0; limbs_mul_to_out_scratch_len(us1_len, lehmer_ss.len())];
limbs_mul_to_out(ss, &us1[..us1_len], lehmer_ss, &mut mul_scratch);
us_len = us1_len + lehmer_us_len;
assert!(us_len <= scratch_len);
if ss[us_len - 1] == 0 {
us_len -= 1;
}
if lehmer_vs_len != 0 {
let mut mul_scratch = vec![0; limbs_mul_to_out_scratch_len(us0.len(), lehmer_vs_len)];
limbs_mul_to_out(us1, us0, &lehmer_vs[..lehmer_vs_len], &mut mul_scratch);
us1_len = us0_len + lehmer_vs_len;
if us1[us1_len - 1] == 0 {
us1_len -= 1;
}
let us1 = &us1[..us1_len];
let carry = if us1_len <= us_len {
limbs_slice_add_greater_in_place_left(&mut ss[..us_len], us1)
} else {
let old_us_len = us_len;
us_len = us1_len;
limbs_add_to_out_aliased(&mut ss[..us1_len], old_us_len, us1)
};
ss[us_len] = Limb::from(carry);
if carry {
us_len += 1;
}
assert!(us_len < scratch_len);
}
(gs_len, lehmer_us_sign)
}
}
fn extended_gcd_helper(a: Natural, b: Natural) -> (Natural, Integer, Integer) {
let mut xs = a.to_limbs_asc();
let mut ys = b.to_limbs_asc();
let mut a = Integer::from(a);
let mut b = Integer::from(b);
let mut swapped = false;
if xs.len() < ys.len() {
swap(&mut xs, &mut ys);
swap(&mut a, &mut b);
swapped = true;
}
let mut gs = vec![0; ys.len()];
let mut ss = vec![0; ys.len() + 1];
let (g_len, ss_sign) = limbs_extended_gcd(&mut gs, &mut ss, &mut xs, &mut ys);
gs.truncate(g_len);
let gcd = Natural::from_owned_limbs_asc(gs);
let mut s = Integer::from_sign_and_abs(ss_sign, Natural::from_owned_limbs_asc(ss));
let mut t = (Integer::from(&gcd) - a * &s).div_exact(b);
if swapped {
swap(&mut s, &mut t);
}
(gcd, s, t)
}
impl ExtendedGcd for Natural {
type Gcd = Self;
type Cofactor = Integer;
fn extended_gcd(self, other: Self) -> (Self, Integer, Integer) {
match (self, other) {
(Self::ZERO, Self::ZERO) => (Self::ZERO, Integer::ZERO, Integer::ZERO),
(a, b) if a == b => (b, Integer::ZERO, Integer::ONE),
(Self::ZERO, b) => (b, Integer::ZERO, Integer::ONE),
(a, Self::ZERO) => (a, Integer::ONE, Integer::ZERO),
(Self(Small(x)), Self(Small(y))) => {
let (gcd, s, t) = x.extended_gcd(y);
(Self::from(gcd), Integer::from(s), Integer::from(t))
}
(a, b) => extended_gcd_helper(a, b),
}
}
}
impl<'a> ExtendedGcd<&'a Self> for Natural {
type Gcd = Self;
type Cofactor = Integer;
fn extended_gcd(self, other: &'a Self) -> (Self, Integer, Integer) {
match (self, other) {
(Self::ZERO, &Self::ZERO) => (Self::ZERO, Integer::ZERO, Integer::ZERO),
(a, b) if a == *b => (b.clone(), Integer::ZERO, Integer::ONE),
(Self::ZERO, b) => (b.clone(), Integer::ZERO, Integer::ONE),
(a, &Self::ZERO) => (a, Integer::ONE, Integer::ZERO),
(Self(Small(x)), Self(Small(y))) => {
let (gcd, s, t) = x.extended_gcd(*y);
(Self::from(gcd), Integer::from(s), Integer::from(t))
}
(a, b) => extended_gcd_helper(a, b.clone()),
}
}
}
impl ExtendedGcd<Natural> for &Natural {
type Gcd = Natural;
type Cofactor = Integer;
fn extended_gcd(self, other: Natural) -> (Natural, Integer, Integer) {
match (self, other) {
(&Natural::ZERO, Natural::ZERO) => (Natural::ZERO, Integer::ZERO, Integer::ZERO),
(a, b) if *a == b => (b, Integer::ZERO, Integer::ONE),
(&Natural::ZERO, b) => (b, Integer::ZERO, Integer::ONE),
(a, Natural::ZERO) => (a.clone(), Integer::ONE, Integer::ZERO),
(Natural(Small(x)), Natural(Small(y))) => {
let (gcd, s, t) = x.extended_gcd(y);
(Natural::from(gcd), Integer::from(s), Integer::from(t))
}
(a, b) => extended_gcd_helper(a.clone(), b),
}
}
}
impl ExtendedGcd<&Natural> for &Natural {
type Gcd = Natural;
type Cofactor = Integer;
fn extended_gcd(self, other: &Natural) -> (Natural, Integer, Integer) {
match (self, other) {
(&Natural::ZERO, &Natural::ZERO) => (Natural::ZERO, Integer::ZERO, Integer::ZERO),
(a, b) if a == b => (b.clone(), Integer::ZERO, Integer::ONE),
(&Natural::ZERO, b) => (b.clone(), Integer::ZERO, Integer::ONE),
(a, &Natural::ZERO) => (a.clone(), Integer::ONE, Integer::ZERO),
(Natural(Small(x)), Natural(Small(y))) => {
let (gcd, s, t) = x.extended_gcd(*y);
(Natural::from(gcd), Integer::from(s), Integer::from(t))
}
(a, b) => extended_gcd_helper(a.clone(), b.clone()),
}
}
}