use crate::natural::InnerNatural::{Large, Small};
use crate::natural::arithmetic::eq_mod::limbs_mod_exact_odd_limb;
use crate::natural::arithmetic::gcd::half_gcd::limbs_gcd_reduced;
use crate::natural::arithmetic::mod_op::limbs_mod_limb_alt_2;
use crate::natural::arithmetic::shr::limbs_slice_shr_in_place;
use crate::natural::comparison::cmp::limbs_cmp;
use crate::natural::{Natural, limb_to_bit_count};
use crate::platform::{BMOD_1_TO_MOD_1_THRESHOLD, DoubleLimb, Limb};
use core::cmp::{Ordering::*, min};
use core::mem::swap;
use malachite_base::num::arithmetic::traits::{Gcd, GcdAssign};
use malachite_base::num::basic::traits::Zero;
use malachite_base::num::logic::traits::TrailingZeros;
use malachite_base::slices::slice_leading_zeros;
fn limbs_mod_or_modexact(ns: &[Limb], d: Limb) -> Limb {
if ns.len() < BMOD_1_TO_MOD_1_THRESHOLD {
limbs_mod_exact_odd_limb(ns, d, 0)
} else {
limbs_mod_limb_alt_2::<DoubleLimb, Limb>(ns, d)
}
}
pub_test! {limbs_gcd_limb(xs: &[Limb], mut y: Limb) -> Limb {
assert!(xs.len() > 1);
assert_ne!(y, 0);
let mut x = xs[0];
let mut zeros = y.trailing_zeros();
y >>= zeros;
if x != 0 {
zeros = min(zeros, x.trailing_zeros());
}
x = limbs_mod_or_modexact(xs, y);
if x != 0 {
y.gcd_assign(x >> x.trailing_zeros());
}
y << zeros
}}
fn gcd_greater_helper(mut xs: &mut [Limb], mut ys: &mut [Limb]) -> Natural {
let xs_zero_limbs = slice_leading_zeros(xs);
let ys_zero_limbs = slice_leading_zeros(ys);
let common_zero_limbs = min(xs_zero_limbs, ys_zero_limbs);
xs = &mut xs[common_zero_limbs..];
ys = &mut ys[common_zero_limbs..];
let xs_zero_bits = TrailingZeros::trailing_zeros(xs[0]);
let ys_zero_bits = TrailingZeros::trailing_zeros(ys[0]);
let common_zero_bits = min(xs_zero_bits, ys_zero_bits);
if common_zero_bits != 0 {
limbs_slice_shr_in_place(xs, common_zero_bits);
limbs_slice_shr_in_place(ys, common_zero_bits);
if *xs.last().unwrap() == 0 {
let n = xs.len();
xs = &mut xs[..n - 1];
}
if *ys.last().unwrap() == 0 {
let n = ys.len();
ys = &mut ys[..n - 1];
}
}
let n = if ys.len() == 1 {
Natural::from(if xs.len() == 1 {
xs[0].gcd(ys[0])
} else {
limbs_gcd_limb(xs, ys[0])
})
} else {
let mut out = vec![0; xs.len()];
let out_len = limbs_gcd_reduced(&mut out, xs, ys);
out.resize(out_len, 0);
Natural::from_owned_limbs_asc(out)
};
n << (limb_to_bit_count(common_zero_limbs) + common_zero_bits)
}
impl Gcd<Self> for Natural {
type Output = Self;
fn gcd(mut self, other: Self) -> Self {
self.gcd_assign(other);
self
}
}
impl<'a> Gcd<&'a Self> for Natural {
type Output = Self;
#[inline]
fn gcd(mut self, other: &'a Self) -> Self {
self.gcd_assign(other);
self
}
}
impl Gcd<Natural> for &Natural {
type Output = Natural;
#[inline]
fn gcd(self, mut other: Natural) -> Natural {
other.gcd_assign(self);
other
}
}
impl Gcd<&Natural> for &Natural {
type Output = Natural;
#[inline]
fn gcd(self, other: &Natural) -> Natural {
match (self, other) {
(x, &Natural::ZERO) => x.clone(),
(&Natural::ZERO, y) => y.clone(),
(x, y) if core::ptr::eq(x, y) => x.clone(),
(Natural(Small(x)), Natural(Small(y))) => Natural::from(x.gcd(*y)),
(Natural(Large(xs)), Natural(Small(y))) => Natural::from(limbs_gcd_limb(xs, *y)),
(Natural(Small(x)), Natural(Large(ys))) => Natural::from(limbs_gcd_limb(ys, *x)),
(Natural(Large(xs)), Natural(Large(ys))) => {
let c = limbs_cmp(xs, ys);
if c == Equal {
return self.clone();
}
let mut xs = xs.clone();
let mut xs: &mut [Limb] = &mut xs;
let mut ys = ys.clone();
let mut ys: &mut [Limb] = &mut ys;
if c == Less {
swap(&mut xs, &mut ys);
}
gcd_greater_helper(xs, ys)
}
}
}
}
impl GcdAssign<Self> for Natural {
#[inline]
fn gcd_assign(&mut self, other: Self) {
match (&mut *self, other) {
(_, Self::ZERO) => {}
(&mut Self::ZERO, y) => *self = y,
(Self(Small(x)), Self(Small(y))) => x.gcd_assign(y),
(Self(Large(xs)), Self(Small(y))) => {
*self = Self::from(limbs_gcd_limb(xs, y));
}
(Self(Small(x)), Self(Large(ys))) => {
*self = Self::from(limbs_gcd_limb(&ys, *x));
}
(Self(Large(xs)), Self(Large(mut ys))) => {
let mut xs: &mut [Limb] = &mut *xs;
let mut ys: &mut [Limb] = &mut ys;
match limbs_cmp(xs, ys) {
Equal => return,
Less => {
swap(&mut xs, &mut ys);
}
_ => {}
}
*self = gcd_greater_helper(xs, ys);
}
}
}
}
impl<'a> GcdAssign<&'a Self> for Natural {
#[inline]
fn gcd_assign(&mut self, other: &'a Self) {
match (&mut *self, other) {
(_, &Self::ZERO) => {}
(&mut Self::ZERO, y) => self.clone_from(y),
(Self(Small(x)), Self(Small(y))) => x.gcd_assign(*y),
(Self(Large(xs)), Self(Small(y))) => {
*self = Self::from(limbs_gcd_limb(xs, *y));
}
(Self(Small(x)), Self(Large(ys))) => {
*self = Self::from(limbs_gcd_limb(ys, *x));
}
(Self(Large(xs)), Self(Large(ys))) => {
let c = limbs_cmp(xs, ys);
if c == Equal {
return;
}
let mut xs: &mut [Limb] = &mut *xs;
let mut ys = ys.clone();
let mut ys: &mut [Limb] = &mut ys;
if c == Less {
swap(&mut xs, &mut ys);
}
*self = gcd_greater_helper(xs, ys);
}
}
}
}
pub mod extended_gcd;
pub mod half_gcd;
pub mod matrix_2_2;