use crate::natural::InnerNatural::{Large, Small};
use crate::natural::Natural;
use crate::natural::arithmetic::div_exact::{
limbs_modular_div_mod_barrett, limbs_modular_div_mod_barrett_scratch_len,
limbs_modular_div_mod_divide_and_conquer, limbs_modular_div_mod_schoolbook,
limbs_modular_invert_limb,
};
use crate::natural::arithmetic::eq_mod::limbs_mod_exact_odd_limb;
use crate::natural::arithmetic::mod_op::limbs_mod_limb;
use crate::natural::arithmetic::shr::{limbs_shr_to_out, limbs_slice_shr_in_place};
use crate::platform::{
BMOD_1_TO_MOD_1_THRESHOLD, DC_BDIV_QR_THRESHOLD, DoubleLimb, Limb, MU_BDIV_QR_THRESHOLD,
};
use alloc::vec::Vec;
use malachite_base::num::arithmetic::traits::{DivisibleBy, DivisibleByPowerOf2, Parity};
use malachite_base::num::basic::integers::PrimitiveInt;
use malachite_base::num::basic::traits::Zero;
use malachite_base::num::logic::traits::TrailingZeros;
use malachite_base::slices::{slice_leading_zeros, slice_test_zero};
pub_crate_test! {limbs_divisible_by_limb(ns: &[Limb], d: Limb) -> bool {
assert!(ns.len() > 1);
if d.even() {
let twos = TrailingZeros::trailing_zeros(d);
ns[0].divisible_by_power_of_2(twos) && limbs_mod_exact_odd_limb(ns, d >> twos, 0) == 0
} else {
limbs_mod_exact_odd_limb(ns, d, 0) == 0
}
}}
fn limbs_mod_limb_helper(ns: &[Limb], d_low: Limb) -> Limb {
if ns.len() < BMOD_1_TO_MOD_1_THRESHOLD {
limbs_mod_exact_odd_limb(ns, d_low, 0)
} else {
limbs_mod_limb::<DoubleLimb, Limb>(ns, d_low)
}
}
pub_crate_test! {
#[allow(clippy::absurd_extreme_comparisons)]
limbs_divisible_by(ns: &mut [Limb], ds: &mut [Limb]) -> bool {
let n_len = ns.len();
let d_len = ds.len();
assert_ne!(d_len, 0);
assert!(n_len >= d_len);
assert_ne!(*ns.last().unwrap(), 0);
assert_ne!(*ds.last().unwrap(), 0);
let offset = slice_leading_zeros(ds);
let (ns_lo, ns) = ns.split_at_mut(offset);
if !slice_test_zero(ns_lo) {
return false;
}
let n_len = ns.len();
let ds = &mut ds[offset..];
let d_len = ds.len();
let n_0 = ns[0];
let d_0 = ds[0];
let d_mask = (d_0 & d_0.wrapping_neg()).wrapping_sub(1);
if n_0 & d_mask != 0 {
return false;
}
if d_len == 1 {
return if n_len >= BMOD_1_TO_MOD_1_THRESHOLD {
limbs_mod_limb::<DoubleLimb, Limb>(ns, d_0) == 0
} else {
limbs_mod_exact_odd_limb(ns, d_0 >> d_0.trailing_zeros(), 0) == 0
};
}
let trailing_zeros = TrailingZeros::trailing_zeros(d_0);
if d_len == 2 {
let d_1 = ds[1];
if d_1 <= d_mask {
let d_low = (d_0 >> trailing_zeros) | (d_1 << (Limb::WIDTH - trailing_zeros));
return limbs_mod_limb_helper(ns, d_low) == 0;
}
}
let n_len_plus_1 = n_len + 1;
let mut qs = vec![0; n_len_plus_1 - d_len];
if trailing_zeros != 0 {
assert_eq!(limbs_slice_shr_in_place(ds, trailing_zeros), 0);
assert_eq!(limbs_slice_shr_in_place(ns, trailing_zeros), 0);
}
let mut rs_vec;
let rs = if ns[n_len - 1] >= ds[d_len - 1] {
rs_vec = Vec::with_capacity(n_len_plus_1);
rs_vec.extend_from_slice(ns);
rs_vec.push(0);
&mut rs_vec
} else if n_len == d_len {
return false;
} else {
ns
};
let r_len = rs.len();
let q_len = r_len - d_len;
let rs = if d_len < DC_BDIV_QR_THRESHOLD || q_len < DC_BDIV_QR_THRESHOLD {
let d_inv = limbs_modular_invert_limb(ds[0]).wrapping_neg();
limbs_modular_div_mod_schoolbook(&mut qs, rs, ds, d_inv);
&mut rs[q_len..]
} else if d_len < MU_BDIV_QR_THRESHOLD {
let d_inv = limbs_modular_invert_limb(ds[0]).wrapping_neg();
limbs_modular_div_mod_divide_and_conquer(&mut qs, rs, ds, d_inv);
&mut rs[q_len..]
} else {
let mut scratch = vec![0; limbs_modular_div_mod_barrett_scratch_len(r_len, d_len)];
let ns = rs.to_vec();
limbs_modular_div_mod_barrett(&mut qs, rs, &ns, ds, &mut scratch);
&mut rs[..d_len]
};
slice_test_zero(rs)
}}
pub_crate_test! {
#[allow(clippy::absurd_extreme_comparisons)]
limbs_divisible_by_val_ref(ns: &mut [Limb], ds: &[Limb]) -> bool {
let n_len = ns.len();
let d_len = ds.len();
assert_ne!(d_len, 0);
assert!(n_len >= d_len);
assert_ne!(*ns.last().unwrap(), 0);
assert_ne!(*ds.last().unwrap(), 0);
let offset = slice_leading_zeros(ds);
let (ns_lo, ns) = ns.split_at_mut(offset);
if !slice_test_zero(ns_lo) {
return false;
}
let n_len = ns.len();
let mut scratch;
let mut ds = &ds[offset..];
let d_len = ds.len();
let n_0 = ns[0];
let d_0 = ds[0];
let d_mask = (d_0 & d_0.wrapping_neg()).wrapping_sub(1);
if n_0 & d_mask != 0 {
return false;
}
if d_len == 1 {
return if n_len >= BMOD_1_TO_MOD_1_THRESHOLD {
limbs_mod_limb::<DoubleLimb, Limb>(ns, d_0) == 0
} else {
limbs_mod_exact_odd_limb(ns, d_0 >> d_0.trailing_zeros(), 0) == 0
};
}
let trailing_zeros = TrailingZeros::trailing_zeros(d_0);
if d_len == 2 {
let d_1 = ds[1];
if d_1 <= d_mask {
let d_low = (d_0 >> trailing_zeros) | (d_1 << (Limb::WIDTH - trailing_zeros));
return limbs_mod_limb_helper(ns, d_low) == 0;
}
}
let n_len_plus_1 = n_len + 1;
let mut qs = vec![0; n_len_plus_1 - d_len];
if trailing_zeros != 0 {
scratch = vec![0; d_len];
assert_eq!(limbs_shr_to_out(&mut scratch, ds, trailing_zeros), 0);
ds = &scratch;
assert_eq!(limbs_slice_shr_in_place(ns, trailing_zeros), 0);
}
let mut rs_vec;
let rs = if ns[n_len - 1] >= ds[d_len - 1] {
rs_vec = Vec::with_capacity(n_len_plus_1);
rs_vec.extend_from_slice(ns);
rs_vec.push(0);
&mut rs_vec
} else if n_len == d_len {
return false;
} else {
ns
};
let r_len = rs.len();
let q_len = r_len - d_len;
let rs = if d_len < DC_BDIV_QR_THRESHOLD || q_len < DC_BDIV_QR_THRESHOLD {
let d_inv = limbs_modular_invert_limb(ds[0]).wrapping_neg();
limbs_modular_div_mod_schoolbook(&mut qs, rs, ds, d_inv);
&mut rs[q_len..]
} else if d_len < MU_BDIV_QR_THRESHOLD {
let d_inv = limbs_modular_invert_limb(ds[0]).wrapping_neg();
limbs_modular_div_mod_divide_and_conquer(&mut qs, rs, ds, d_inv);
&mut rs[q_len..]
} else {
let mut scratch = vec![0; limbs_modular_div_mod_barrett_scratch_len(r_len, d_len)];
let ns = rs.to_vec();
limbs_modular_div_mod_barrett(&mut qs, rs, &ns, ds, &mut scratch);
&mut rs[..d_len]
};
slice_test_zero(rs)
}}
pub_test! {
#[allow(clippy::absurd_extreme_comparisons)]
limbs_divisible_by_ref_val(ns: &[Limb], ds: &mut [Limb]) -> bool {
let n_len = ns.len();
let d_len = ds.len();
assert_ne!(d_len, 0);
assert!(n_len >= d_len);
assert_ne!(*ns.last().unwrap(), 0);
assert_ne!(*ds.last().unwrap(), 0);
let offset = slice_leading_zeros(ds);
let (ns_lo, ns) = ns.split_at(offset);
if !slice_test_zero(ns_lo) {
return false;
}
let n_len = ns.len();
let ds = &mut ds[offset..];
let d_len = ds.len();
let n_0 = ns[0];
let d_0 = ds[0];
let d_mask = (d_0 & d_0.wrapping_neg()).wrapping_sub(1);
if n_0 & d_mask != 0 {
return false;
}
if d_len == 1 {
return if n_len >= BMOD_1_TO_MOD_1_THRESHOLD {
limbs_mod_limb::<DoubleLimb, Limb>(ns, d_0) == 0
} else {
limbs_mod_exact_odd_limb(ns, d_0 >> d_0.trailing_zeros(), 0) == 0
};
}
let trailing_zeros = TrailingZeros::trailing_zeros(d_0);
if d_len == 2 {
let d_1 = ds[1];
if d_1 <= d_mask {
let d_low = (d_0 >> trailing_zeros) | (d_1 << (Limb::WIDTH - trailing_zeros));
return limbs_mod_limb_helper(ns, d_low) == 0;
}
}
let n_len_plus_1 = n_len + 1;
let mut rs_qs = vec![0; (n_len_plus_1 << 1) - d_len];
let (rs, qs) = rs_qs.split_at_mut(n_len_plus_1);
if trailing_zeros != 0 {
assert_eq!(limbs_slice_shr_in_place(ds, trailing_zeros), 0);
assert_eq!(limbs_shr_to_out(rs, ns, trailing_zeros), 0);
} else {
rs[..n_len].copy_from_slice(ns);
}
let r_len = if rs[n_len - 1] >= ds[d_len - 1] {
n_len_plus_1
} else if n_len == d_len {
return false;
} else {
n_len
};
let rs = &mut rs[..r_len];
let q_len = r_len - d_len;
let rs = if d_len < DC_BDIV_QR_THRESHOLD || q_len < DC_BDIV_QR_THRESHOLD {
let d_inv = limbs_modular_invert_limb(ds[0]).wrapping_neg();
limbs_modular_div_mod_schoolbook(qs, rs, ds, d_inv);
&mut rs[q_len..]
} else if d_len < MU_BDIV_QR_THRESHOLD {
let d_inv = limbs_modular_invert_limb(ds[0]).wrapping_neg();
limbs_modular_div_mod_divide_and_conquer(qs, rs, ds, d_inv);
&mut rs[q_len..]
} else {
let mut scratch = vec![0; limbs_modular_div_mod_barrett_scratch_len(r_len, d_len)];
let ns = rs.to_vec();
limbs_modular_div_mod_barrett(qs, rs, &ns, ds, &mut scratch);
&mut rs[..d_len]
};
slice_test_zero(rs)
}}
pub_test! {
#[allow(clippy::absurd_extreme_comparisons)]
limbs_divisible_by_ref_ref(ns: &[Limb], ds: &[Limb]) -> bool {
let n_len = ns.len();
let d_len = ds.len();
assert_ne!(d_len, 0);
assert!(n_len >= d_len);
assert_ne!(*ns.last().unwrap(), 0);
assert_ne!(*ds.last().unwrap(), 0);
let offset = slice_leading_zeros(ds);
let (ns_lo, ns) = ns.split_at(offset);
if !slice_test_zero(ns_lo) {
return false;
}
let n_len = ns.len();
let mut scratch;
let mut ds = &ds[offset..];
let d_len = ds.len();
let d_0 = ds[0];
let d_mask = (d_0 & d_0.wrapping_neg()).wrapping_sub(1);
if ns[0] & d_mask != 0 {
return false;
}
if d_len == 1 {
return if n_len >= BMOD_1_TO_MOD_1_THRESHOLD {
limbs_mod_limb::<DoubleLimb, Limb>(ns, d_0) == 0
} else {
limbs_mod_exact_odd_limb(ns, d_0 >> d_0.trailing_zeros(), 0) == 0
};
}
let trailing_zeros = TrailingZeros::trailing_zeros(d_0);
if d_len == 2 {
let d_1 = ds[1];
if d_1 <= d_mask {
let d_low = (d_0 >> trailing_zeros) | (d_1 << (Limb::WIDTH - trailing_zeros));
return limbs_mod_limb_helper(ns, d_low) == 0;
}
}
let n_len_plus_1 = n_len + 1;
let mut rs_qs = vec![0; (n_len_plus_1 << 1) - d_len];
let (rs, qs) = rs_qs.split_at_mut(n_len_plus_1);
if trailing_zeros != 0 {
scratch = vec![0; d_len];
assert_eq!(limbs_shr_to_out(&mut scratch, ds, trailing_zeros), 0);
ds = &scratch;
assert_eq!(limbs_shr_to_out(rs, ns, trailing_zeros), 0);
} else {
rs[..n_len].copy_from_slice(ns);
}
let r_len = if rs[n_len - 1] >= ds[d_len - 1] {
n_len_plus_1
} else if n_len == d_len {
return false;
} else {
n_len
};
let rs = &mut rs[..r_len];
let q_len = r_len - d_len;
let rs = if d_len < DC_BDIV_QR_THRESHOLD || q_len < DC_BDIV_QR_THRESHOLD {
let d_inv = limbs_modular_invert_limb(ds[0]).wrapping_neg();
limbs_modular_div_mod_schoolbook(qs, rs, ds, d_inv);
&mut rs[q_len..]
} else if d_len < MU_BDIV_QR_THRESHOLD {
let d_inv = limbs_modular_invert_limb(ds[0]).wrapping_neg();
limbs_modular_div_mod_divide_and_conquer(qs, rs, ds, d_inv);
&mut rs[q_len..]
} else {
let mut scratch = vec![0; limbs_modular_div_mod_barrett_scratch_len(r_len, d_len)];
let ns = rs.to_vec();
limbs_modular_div_mod_barrett(qs, rs, &ns, ds, &mut scratch);
&mut rs[..d_len]
};
slice_test_zero(rs)
}}
impl Natural {
fn divisible_by_limb(&self, other: Limb) -> bool {
match (self, other) {
(&Self::ZERO, _) => true,
(_, 0) => false,
(&Self(Small(small)), y) => small.divisible_by(y),
(Self(Large(limbs)), y) => limbs_divisible_by_limb(limbs, y),
}
}
fn limb_divisible_by_natural(&self, other: Limb) -> bool {
match (other, self) {
(0, _) => true,
(_, &Self::ZERO | &Self(Large(_))) => false,
(x, &Self(Small(small))) => x.divisible_by(small),
}
}
}
impl DivisibleBy<Self> for Natural {
fn divisible_by(mut self, mut other: Self) -> bool {
match (&mut self, &mut other) {
(x, &mut Self(Small(y))) => x.divisible_by_limb(y),
(&mut Self(Small(x)), y) => y.limb_divisible_by_natural(x),
(Self(Large(xs)), Self(Large(ys))) => {
xs.len() >= ys.len() && limbs_divisible_by(xs, ys)
}
}
}
}
impl<'a> DivisibleBy<&'a Self> for Natural {
fn divisible_by(mut self, other: &'a Self) -> bool {
match (&mut self, other) {
(x, &Self(Small(y))) => x.divisible_by_limb(y),
(&mut Self(Small(x)), y) => y.limb_divisible_by_natural(x),
(Self(Large(xs)), Self(Large(ys))) => {
xs.len() >= ys.len() && limbs_divisible_by_val_ref(xs, ys)
}
}
}
}
impl DivisibleBy<Natural> for &Natural {
fn divisible_by(self, mut other: Natural) -> bool {
match (self, &mut other) {
(x, &mut Natural(Small(y))) => x.divisible_by_limb(y),
(&Natural(Small(x)), y) => y.limb_divisible_by_natural(x),
(Natural(Large(xs)), Natural(Large(ys))) => {
xs.len() >= ys.len() && limbs_divisible_by_ref_val(xs, ys)
}
}
}
}
impl DivisibleBy<&Natural> for &Natural {
fn divisible_by(self, other: &Natural) -> bool {
match (self, other) {
(x, &Natural(Small(y))) => x.divisible_by_limb(y),
(&Natural(Small(x)), y) => y.limb_divisible_by_natural(x),
(Natural(Large(xs)), Natural(Large(ys))) => {
xs.len() >= ys.len() && limbs_divisible_by_ref_ref(xs, ys)
}
}
}
}