use dashu_base::{DivRem, Sign, UnsignedAbs};
use num_modular::{DivExact, DivExactAssign};
use crate::{
add,
arch::word::{DoubleWord, Word},
ibig::IBig,
math::inv_mod_pow2,
mul::{sub_mul_dword_same_len_in_place, sub_mul_word_same_len_in_place},
primitive::{extend_word, shrink_dword, WORD_BITS},
repr::{TypedRepr, TypedReprRef},
ubig::UBig,
};
const THRESHOLD_DIV_EXACT_DEFAULT: usize = 180;
mod threshold {
#[inline]
pub fn div_exact() -> usize {
#[cfg(feature = "tuning")]
{
if let Ok(s) = std::env::var("DASHU_THRESHOLD_DIV_EXACT") {
if let Ok(v) = s.parse::<usize>() {
return v;
}
}
}
super::THRESHOLD_DIV_EXACT_DEFAULT
}
}
impl UBig {
fn div_exact_assign_dword(&mut self, divisor: DoubleWord) -> bool {
if divisor == 0 {
return false; }
if self.is_zero() || divisor == 1 {
return true; }
if shrink_dword(divisor).is_some() {
if !self.repr().is_multiple_of(TypedReprRef::RefSmall(divisor)) {
return false;
}
let taken = core::mem::take(self);
let q = taken
.into_repr()
.div_exact(TypedRepr::Small(divisor), &())
.expect("the probe passed, so the division is exact");
*self = UBig(q);
return true;
}
let backup = self.clone();
let taken = core::mem::take(self);
match taken.into_repr().div_exact(TypedRepr::Small(divisor), &()) {
Some(q) => {
*self = UBig(q);
true
}
None => {
*self = backup;
false
}
}
}
}
pub(crate) mod repr {
use super::*;
use crate::{
arch::word::{DoubleWord, Word},
buffer::Buffer,
div,
math::inv_mod_pow2,
primitive::{extend_word, shrink_dword, split_dword, WORD_BITS, WORD_BITS_USIZE},
repr::{Repr, TypedRepr, TypedReprRef},
shift,
ubig::UBig,
};
impl DivExact<TypedRepr, ()> for TypedRepr {
type Output = Repr;
#[inline]
fn div_exact(self, rhs: TypedRepr, _: &()) -> Option<Repr> {
match (self, rhs) {
(TypedRepr::Small(dword0), TypedRepr::Small(dword1)) => {
div_exact_dword(dword0, dword1)
}
(TypedRepr::Small(_), TypedRepr::Large(_)) => None, (TypedRepr::Large(buffer0), TypedRepr::Small(dword1)) => {
if let Some(word) = shrink_dword(dword1) {
div_exact_large_word(buffer0, word)
} else {
div_exact_large_dword(buffer0, dword1)
}
}
(TypedRepr::Large(buffer0), TypedRepr::Large(buffer1)) => {
div_exact_large(buffer0, buffer1)
}
}
}
}
impl<'l> DivExact<TypedRepr, ()> for TypedReprRef<'l> {
type Output = Repr;
#[inline]
fn div_exact(self, rhs: TypedRepr, _: &()) -> Option<Repr> {
match (self, rhs) {
(TypedReprRef::RefSmall(dword0), TypedRepr::Small(dword1)) => {
div_exact_dword(dword0, dword1)
}
(TypedReprRef::RefSmall(_), TypedRepr::Large(_)) => None,
(TypedReprRef::RefLarge(words0), TypedRepr::Small(dword1)) => {
if let Some(word) = shrink_dword(dword1) {
div_exact_large_word(words0.into(), word)
} else {
div_exact_large_dword(words0.into(), dword1)
}
}
(TypedReprRef::RefLarge(words0), TypedRepr::Large(buffer1)) => {
div_exact_large(words0.into(), buffer1)
}
}
}
}
impl<'r> DivExact<TypedReprRef<'r>, ()> for TypedRepr {
type Output = Repr;
#[inline]
fn div_exact(self, rhs: TypedReprRef, _: &()) -> Option<Repr> {
match (self, rhs) {
(TypedRepr::Small(dword0), TypedReprRef::RefSmall(dword1)) => {
div_exact_dword(dword0, dword1)
}
(TypedRepr::Small(_), TypedReprRef::RefLarge(_)) => None,
(TypedRepr::Large(buffer0), TypedReprRef::RefSmall(dword1)) => {
if let Some(word) = shrink_dword(dword1) {
div_exact_large_word(buffer0, word)
} else {
div_exact_large_dword(buffer0, dword1)
}
}
(TypedRepr::Large(buffer0), TypedReprRef::RefLarge(words1)) => {
div_exact_large(buffer0, words1.into())
}
}
}
}
impl<'l, 'r> DivExact<TypedReprRef<'r>, ()> for TypedReprRef<'l> {
type Output = Repr;
#[inline]
fn div_exact(self, rhs: TypedReprRef, _: &()) -> Option<Repr> {
match (self, rhs) {
(TypedReprRef::RefSmall(dword0), TypedReprRef::RefSmall(dword1)) => {
div_exact_dword(dword0, dword1)
}
(TypedReprRef::RefSmall(_), TypedReprRef::RefLarge(_)) => None,
(TypedReprRef::RefLarge(words0), TypedReprRef::RefSmall(dword1)) => {
if let Some(word) = shrink_dword(dword1) {
div_exact_large_word(words0.into(), word)
} else {
div_exact_large_dword(words0.into(), dword1)
}
}
(TypedReprRef::RefLarge(words0), TypedReprRef::RefLarge(words1)) => {
div_exact_large(words0.into(), words1.into())
}
}
}
}
#[inline]
fn div_exact_dword(lhs: DoubleWord, rhs: DoubleWord) -> Option<Repr> {
if rhs == 0 {
None
} else if rhs == 1 {
Some(Repr::from_dword(lhs))
} else if lhs % rhs == 0 {
Some(Repr::from_dword(lhs / rhs))
} else {
None
}
}
fn div_exact_large_word(mut buffer: Buffer, d: Word) -> Option<Repr> {
if d == 0 {
return None; }
if d == 1 {
return Some(Repr::from_buffer(buffer));
}
let trailing = d.trailing_zeros();
let d_odd = d >> trailing;
if d_odd == 1 {
if trailing_zeros(&buffer) >= trailing as usize {
shift::shr_in_place(&mut buffer, trailing);
return Some(Repr::from_buffer(buffer));
}
return None;
}
if trailing > 0 && trailing_zeros(&buffer) < trailing as usize {
return None;
}
let di = inv_mod_pow2(extend_word(d_odd), WORD_BITS) as Word;
if !hensel_div_odd_in_place(&mut buffer, d_odd, di) {
return None;
}
if trailing > 0 {
shift::shr_in_place(&mut buffer, trailing);
}
Some(Repr::from_buffer(buffer))
}
fn div_exact_large_dword(mut buffer: Buffer, d: DoubleWord) -> Option<Repr> {
debug_assert!(shrink_dword(d).is_none()); let trailing = d.trailing_zeros();
let d_odd = d >> trailing;
if d_odd == 1 {
if trailing_zeros(&buffer) >= trailing as usize {
shr_erase_front(&mut buffer, trailing as usize);
return Some(Repr::from_buffer(buffer));
}
return None;
}
if trailing > 0 && trailing_zeros(&buffer) < trailing as usize {
return None;
}
if let Some(word) = shrink_dword(d_odd) {
let di = inv_mod_pow2(extend_word(word), WORD_BITS) as Word;
if !hensel_div_odd_in_place(&mut buffer, word, di) {
return None;
}
} else {
let (d_lo, d_hi) = split_dword(d_odd);
let di = inv_mod_pow2(extend_word(d_lo), WORD_BITS) as Word;
if !hensel_div_odd_dword_in_place(&mut buffer, d_lo, d_hi, di) {
return None;
}
}
if trailing > 0 {
shr_erase_front(&mut buffer, trailing as usize);
}
Some(Repr::from_buffer(buffer))
}
fn div_exact_large(mut dividend: Buffer, mut divisor: Buffer) -> Option<Repr> {
if dividend.len() < divisor.len() {
return None; }
if divisor.len() > super::threshold::div_exact() {
let (q, r) =
UBig(Repr::from_buffer(dividend)).div_rem(UBig(Repr::from_buffer(divisor)));
return if r.is_zero() { Some(q.0) } else { None };
}
let s = trailing_zeros(&divisor);
if s > 0 {
if trailing_zeros(÷nd) < s {
return None; }
shr_erase_front(&mut dividend, s);
shr_erase_front(&mut divisor, s);
divisor.pop_zeros();
dividend.pop_zeros();
}
if dividend.len() < divisor.len() {
return None; }
match divisor.len() {
1 => {
let d = divisor[0];
debug_assert!(d & 1 == 1, "the common factors of 2 were already stripped");
let di = inv_mod_pow2(extend_word(d), WORD_BITS) as Word;
if !hensel_div_odd_in_place(&mut dividend, d, di) {
return None;
}
}
2 => {
let (d_lo, d_hi) = (divisor[0], divisor[1]);
debug_assert!(d_lo & 1 == 1, "the common factors of 2 were already stripped");
let di = inv_mod_pow2(extend_word(d_lo), WORD_BITS) as Word;
if !hensel_div_odd_dword_in_place(&mut dividend, d_lo, d_hi, di) {
return None;
}
}
_ => {
if !hensel_div_exact_large(&mut dividend, &divisor) {
return None;
}
}
}
Some(Repr::from_buffer(dividend))
}
impl TypedReprRef<'_> {
pub(crate) fn is_multiple_of(&self, rhs: TypedReprRef) -> bool {
match (self, rhs) {
(TypedReprRef::RefSmall(dword0), TypedReprRef::RefSmall(dword1)) => {
dword1 != 0 && dword0 % dword1 == 0
}
(TypedReprRef::RefSmall(_), TypedReprRef::RefLarge(_)) => false,
(TypedReprRef::RefLarge(words0), TypedReprRef::RefSmall(dword1)) => {
is_multiple_of_dword(words0, dword1)
}
(TypedReprRef::RefLarge(words0), TypedReprRef::RefLarge(words1)) => {
is_multiple_of_large(words0, words1)
}
}
}
}
fn is_multiple_of_word(words: &[Word], d: Word) -> bool {
if d == 0 {
return false; }
let trailing = d.trailing_zeros();
let d_odd = d >> trailing;
if d_odd == 1 {
return trailing_zeros(words) >= trailing as usize;
}
if trailing > 0 && trailing_zeros(words) < trailing as usize {
return false;
}
let di = inv_mod_pow2(extend_word(d_odd), WORD_BITS) as Word;
hensel_is_multiple_of(words, d_odd, di)
}
fn is_multiple_of_dword(words: &[Word], d: DoubleWord) -> bool {
if d == 0 {
return false; }
if let Some(word) = shrink_dword(d) {
return is_multiple_of_word(words, word);
}
let trailing = d.trailing_zeros();
let d_odd = d >> trailing;
if d_odd == 1 {
return trailing_zeros(words) >= trailing as usize;
}
if trailing > 0 && trailing_zeros(words) < trailing as usize {
return false;
}
let mut buffer = words.to_vec();
if let Some(word) = shrink_dword(d_odd) {
let di = inv_mod_pow2(extend_word(word), WORD_BITS) as Word;
hensel_div_odd_in_place(&mut buffer, word, di)
} else {
let (d_lo, d_hi) = split_dword(d_odd);
let di = inv_mod_pow2(extend_word(d_lo), WORD_BITS) as Word;
hensel_div_odd_dword_in_place(&mut buffer, d_lo, d_hi, di)
}
}
fn is_multiple_of_large(words: &[Word], divisor: &[Word]) -> bool {
div_exact_large(Buffer::from(words), Buffer::from(divisor)).is_some()
}
fn trailing_zeros(words: &[Word]) -> usize {
for (i, &w) in words.iter().enumerate() {
if w != 0 {
return i * WORD_BITS_USIZE + w.trailing_zeros() as usize;
}
}
usize::MAX
}
fn shr_erase_front(buffer: &mut Buffer, shift: usize) {
buffer.erase_front(shift / WORD_BITS_USIZE);
if shift % WORD_BITS_USIZE != 0 {
shift::shr_in_place(buffer, (shift % WORD_BITS_USIZE) as u32);
}
}
impl<'a> TypedReprRef<'a> {
pub(crate) const fn is_multiple_of_dword(self, divisor: DoubleWord) -> bool {
if let Some(w) = shrink_dword(divisor) {
match self {
TypedReprRef::RefSmall(dword) => dword % extend_word(w) == 0,
TypedReprRef::RefLarge(words) => div::rem_by_word(words, w) == 0,
}
} else {
match self {
TypedReprRef::RefSmall(dword) => dword % divisor == 0,
TypedReprRef::RefLarge(words) => div::rem_by_dword(words, divisor) == 0,
}
}
}
}
}
pub(crate) fn hensel_div_odd_in_place(words: &mut [Word], d: Word, di: Word) -> bool {
let mut c: Word = 0;
let mut q_last = words[0].wrapping_mul(di);
words[0] = q_last;
for word in words.iter_mut().skip(1) {
let h = ((extend_word(q_last) * extend_word(d)) >> WORD_BITS) as Word;
c = c.wrapping_add(h);
let (l, borrow) = word.overflowing_sub(c);
c = borrow as Word;
q_last = l.wrapping_mul(di);
*word = q_last;
}
let h = ((extend_word(q_last) * extend_word(d)) >> WORD_BITS) as Word;
c == 0 && h == 0
}
pub(crate) fn hensel_is_multiple_of(words: &[Word], d: Word, di: Word) -> bool {
let mut c: Word = 0;
let mut q_last = words[0].wrapping_mul(di);
for word in words.iter().skip(1) {
let h = ((extend_word(q_last) * extend_word(d)) >> WORD_BITS) as Word;
c = c.wrapping_add(h);
let (l, borrow) = word.overflowing_sub(c);
c = borrow as Word;
q_last = l.wrapping_mul(di);
}
let h = ((extend_word(q_last) * extend_word(d)) >> WORD_BITS) as Word;
c == 0 && h == 0
}
pub(crate) fn hensel_div_odd_dword_in_place(
words: &mut [Word],
d_lo: Word,
d_hi: Word,
di: Word,
) -> bool {
let n = words.len();
debug_assert!(n >= 2 && d_lo & 1 == 1);
for i in 0..n - 1 {
let q = words[i].wrapping_mul(di);
let (borrow_lo, borrow_hi) =
sub_mul_dword_same_len_in_place(&mut words[i..i + 2], &[d_lo, d_hi], q, 0);
debug_assert!(borrow_hi == 0, "the total borrow of q·d is at most one word");
if borrow_lo != 0 && (i + 2 >= n || add::sub_word_in_place(&mut words[i + 2..], borrow_lo))
{
return false; }
words[i] = q;
}
words[n - 1] == 0
}
pub(crate) fn hensel_div_exact_large(dividend: &mut [Word], divisor: &[Word]) -> bool {
let n = dividend.len();
let m = divisor.len();
debug_assert!(n >= m && m >= 2 && divisor[0] & 1 == 1);
let qn = n - m + 1;
let di = inv_mod_pow2(extend_word(divisor[0]), WORD_BITS) as Word;
for i in 0..qn {
let q = dividend[i].wrapping_mul(di);
let mut borrow = sub_mul_word_same_len_in_place(&mut dividend[i..i + m], q, divisor);
if borrow != 0 {
for w in dividend[i + m..].iter_mut() {
let (l, b) = w.overflowing_sub(borrow);
*w = l;
borrow = b as Word;
if borrow == 0 {
break;
}
}
}
if borrow != 0 {
return false; }
dividend[i] = q;
}
dividend[qn..].iter().all(|&w| w == 0)
}
impl UBig {
#[inline]
pub fn is_multiple_of(&self, divisor: &Self) -> bool {
assert!(!divisor.is_zero(), "division by zero");
self.repr().is_multiple_of(divisor.repr())
}
#[inline]
pub const fn is_multiple_of_const(&self, divisor: DoubleWord) -> bool {
self.repr().is_multiple_of_dword(divisor)
}
}
impl IBig {
#[inline]
pub fn is_multiple_of(&self, divisor: &Self) -> bool {
self.unsigned_abs().is_multiple_of(&divisor.unsigned_abs())
}
#[inline]
pub const fn is_multiple_of_const(&self, divisor: DoubleWord) -> bool {
let (_, repr) = self.as_sign_repr();
repr.is_multiple_of_dword(divisor)
}
}
impl DivExact<UBig, ()> for UBig {
type Output = UBig;
#[inline]
fn div_exact(self, rhs: UBig, _: &()) -> Option<UBig> {
self.into_repr().div_exact(rhs.into_repr(), &()).map(UBig)
}
}
impl DivExact<UBig, ()> for &UBig {
type Output = UBig;
#[inline]
fn div_exact(self, rhs: UBig, _: &()) -> Option<UBig> {
self.clone().div_exact(rhs, &())
}
}
impl DivExactAssign<UBig, ()> for UBig {
#[inline]
fn div_exact_assign(&mut self, rhs: UBig, _: &()) -> bool {
if let TypedReprRef::RefSmall(dword) = rhs.repr() {
return self.div_exact_assign_dword(dword);
}
let backup = self.clone();
let taken = core::mem::take(self);
match taken.into_repr().div_exact(rhs.into_repr(), &()) {
Some(q) => {
*self = UBig(q);
true
}
None => {
*self = backup;
false
}
}
}
}
macro_rules! impl_div_exact_ubig_with_prim {
($($T:ty)*) => {$(
impl DivExact<$T, ()> for UBig {
type Output = UBig;
#[inline]
fn div_exact(self, rhs: $T, _: &()) -> Option<UBig> {
match DoubleWord::try_from(rhs) {
Ok(dword) => self.into_repr().div_exact(TypedRepr::Small(dword), &()).map(UBig),
Err(_) => DivExact::<UBig, ()>::div_exact(self, UBig::from(rhs), &()),
}
}
}
impl DivExactAssign<$T, ()> for UBig {
#[inline]
fn div_exact_assign(&mut self, rhs: $T, _: &()) -> bool {
match DoubleWord::try_from(rhs) {
Ok(dword) => self.div_exact_assign_dword(dword),
Err(_) => {
let (q, r) = (&*self).div_rem(&UBig::from(rhs));
if r.is_zero() {
*self = q;
true
} else {
false
}
}
}
}
}
)*};
}
impl_div_exact_ubig_with_prim!(u8 u16 u32 u64 u128 usize);
impl DivExact<IBig, ()> for IBig {
type Output = IBig;
fn div_exact(self, rhs: IBig, _: &()) -> Option<IBig> {
let (sign_self, mag_self) = self.into_parts();
let (sign_rhs, mag_rhs) = rhs.into_parts();
let q_mag = mag_self.div_exact(mag_rhs, &())?;
Some(IBig::from_parts(sign_self * sign_rhs, q_mag))
}
}
impl DivExactAssign<IBig, ()> for IBig {
fn div_exact_assign(&mut self, rhs: IBig, _: &()) -> bool {
if let Some(q) = self.clone().div_exact(rhs, &()) {
*self = q;
true
} else {
false
}
}
}
impl DivExact<IBig, ()> for &IBig {
type Output = IBig;
#[inline]
fn div_exact(self, rhs: IBig, _: &()) -> Option<IBig> {
self.clone().div_exact(rhs, &())
}
}
macro_rules! impl_div_exact_ibig_with_prim {
($($T:ty)*) => {$(
impl DivExact<$T, ()> for IBig {
type Output = IBig;
#[inline]
fn div_exact(self, rhs: $T, _: &()) -> Option<IBig> {
let sign = self.sign();
let q_mag = self.unsigned_abs().div_exact(rhs, &())?;
Some(IBig::from_parts(sign, q_mag))
}
}
impl DivExactAssign<$T, ()> for IBig {
#[inline]
fn div_exact_assign(&mut self, rhs: $T, _: &()) -> bool {
if let Some(q) = self.clone().div_exact(rhs, &()) {
*self = q;
true
} else {
false
}
}
}
)*};
}
impl_div_exact_ibig_with_prim!(u8 u16 u32 u64 u128 usize);
macro_rules! impl_div_exact_ibig_with_signed_prim {
($($T:ty)*) => {$(
impl DivExact<$T, ()> for IBig {
type Output = IBig;
#[inline]
fn div_exact(self, rhs: $T, _: &()) -> Option<IBig> {
let sign = if (self.sign() == Sign::Negative) != (rhs < 0) {
Sign::Negative
} else {
Sign::Positive
};
let q_mag = self.unsigned_abs().div_exact(rhs.unsigned_abs(), &())?;
Some(IBig::from_parts(sign, q_mag))
}
}
impl DivExactAssign<$T, ()> for IBig {
#[inline]
fn div_exact_assign(&mut self, rhs: $T, _: &()) -> bool {
if let Some(q) = self.clone().div_exact(rhs, &()) {
*self = q;
true
} else {
false
}
}
}
)*};
}
impl_div_exact_ibig_with_signed_prim!(i8 i16 i32 i64 i128 isize);
#[cfg(test)]
mod tests {
use super::*;
use crate::{
arch::word::Word,
primitive::{extend_word, WORD_BITS_USIZE},
};
#[test]
fn test_div_exact_assign_matches_div() {
use dashu_base::DivExactAssign;
for d in [2u16, 3, 5, 7, 10, 12, 16, 25, 255, 1001] {
let d = d as Word;
for i in 1..10usize {
for rest in [1u8, 5, 7, 11] {
let n = UBig::from(d).pow(i) * rest;
let want = &n / UBig::from_word(d);
let mut got = n;
assert!(got.div_exact_assign(extend_word(d), &()), "d={d} i={i} rest={rest}");
assert_eq!(got, want, "d={d} i={i} rest={rest}");
}
}
let mut n = UBig::from(d).pow(2) + 1u8;
let before = n.clone();
assert!(!n.div_exact_assign(extend_word(d), &()), "d={d}");
assert_eq!(n, before, "d={d}");
}
}
#[test]
fn test_div_exact_matches_div() {
for d in [
UBig::from(10u8).pow(8), (UBig::ONE << 64) + 3u8, (UBig::ONE << 70) * 5u8, ] {
for i in 1..6usize {
let n = d.clone().pow(i) * 7u8;
let (q, r) = (&n).div_rem(&d);
assert!(r.is_zero(), "d={d:?} i={i}");
assert_eq!(n.clone().div_exact(d.clone(), &()), Some(q), "d={d:?} i={i}");
}
let n = d.clone().pow(2) + 1u8;
assert_eq!(n.div_exact(d, &()), None, "d must not divide d^2+1");
}
let big = UBig::from(10u8).pow(50);
for (a, b) in [
(UBig::from(10u8).pow(80) * 7u8, UBig::from(10u8).pow(80)),
(big.clone() * UBig::from(13u8), big.clone()),
(UBig::from(2u8).pow(300) * 3u8, UBig::from(8u8)),
] {
let (q, r) = (&a).div_rem(&b);
assert_eq!(a.div_exact(b, &()), if r.is_zero() { Some(q) } else { None });
}
assert_eq!(UBig::from(7u8).div_exact(3u8, &()), None);
assert_eq!(UBig::from(7u8).div_exact(UBig::from(3u8), &()), None);
assert_eq!(UBig::from(7u8).div_exact(big, &()), None);
}
#[test]
fn test_hensel_div_exact_large_matches_div() {
for d_bits in [70usize, 100, 150, 300] {
let d = (UBig::ONE << d_bits) + 1u8;
let dw = d.as_words().to_vec();
for i in 1..5usize {
let n = d.clone().pow(i) * 12345u16;
let want = &n / &d;
let mut buf = n.as_words().to_vec();
assert!(hensel_div_exact_large(&mut buf, &dw), "d={d_bits} i={i}");
assert_eq!(UBig::from_words(&buf), want, "d={d_bits} i={i}");
}
let n = d.clone().pow(2) + 2u8;
let mut buf = n.as_words().to_vec();
assert!(!hensel_div_exact_large(&mut buf, &dw), "d={d_bits}");
}
for d_bits in [70usize, 130, 300] {
let odd = (UBig::ONE << d_bits) + 5u8;
let d = &odd * UBig::from(16u8);
let n = d.clone().pow(3) * 77u8;
let (q, r) = (&n).div_rem(&d);
assert!(r.is_zero());
assert_eq!(n.clone().div_exact(d.clone(), &()), Some(q));
assert_eq!((odd * 7u8).div_exact(d, &()), None);
}
}
#[test]
fn test_div_exact_assign_dword() {
use dashu_base::DivExactAssign;
let base = extend_word(Word::MAX) * extend_word(Word::MAX); for d in [
base + 2, base + 3, (1 as DoubleWord) << (2 * WORD_BITS_USIZE - 1), ] {
let d_ubig = UBig::from_dword(d);
for i in 1..6usize {
let n = d_ubig.clone().pow(i) * 7u8;
let want = &n / &d_ubig;
let mut got = n;
assert!(got.div_exact_assign(d, &()), "d={d:?} i={i}");
assert_eq!(got, want, "d={d:?} i={i}");
}
let mut n = d_ubig.clone().pow(2) + 1u8;
let before = n.clone();
assert!(!n.div_exact_assign(d, &()), "d={d:?}");
assert_eq!(n, before, "d={d:?}");
}
}
#[test]
fn test_div_exact_trait_impls() {
use dashu_base::{DivExact, DivExactAssign};
let a = UBig::from(10u8).pow(8) * 7u8;
assert_eq!(a.clone().div_exact(UBig::from(10u8).pow(8), &()), Some(UBig::from(7u8)));
assert_eq!(a.div_exact(UBig::from(3u8), &()), None);
assert_eq!(UBig::from(10u8).pow(8).div_exact(10u8, &()), Some(UBig::from(10u8).pow(7)));
assert_eq!(UBig::from(10u8).pow(8).div_exact(10u32, &()), Some(UBig::from(10u8).pow(7)));
assert_eq!(UBig::from(10u8).pow(8).div_exact(10u128, &()), Some(UBig::from(10u8).pow(7)));
let wide = 1u128 << 100; assert_eq!(UBig::from(10u8).pow(8).div_exact(wide, &()), None);
assert_eq!(UBig::from(wide).div_exact(1u128, &()), Some(UBig::from(wide)));
let mut b = UBig::from(10u8).pow(8) * 7u8;
assert!(b.div_exact_assign(10u8, &()));
assert_eq!(b, UBig::from(10u8).pow(7) * 7u8);
assert!(!b.div_exact_assign(3u8, &())); assert_eq!(b, UBig::from(10u8).pow(7) * 7u8);
let d = UBig::from(10u8).pow(50);
let mut c = d.clone().pow(2) * 7u8;
assert!(c.div_exact_assign(d.clone(), &()));
assert_eq!(c, &d * 7u8);
let mut c = d.clone().pow(2) * 7u8;
let before = c.clone();
assert!(!c.div_exact_assign(d.clone() + 1u8, &()));
assert_eq!(c, before);
let ref_a = UBig::from(10u8).pow(8) * 7u8;
assert_eq!((&ref_a).div_exact(UBig::from(10u8).pow(8), &()), Some(UBig::from(7u8)));
assert_eq!((&ref_a).div_exact(UBig::from(3u8), &()), None);
assert_eq!(ref_a, UBig::from(10u8).pow(8) * 7u8); }
#[test]
fn test_div_exact_ibig() {
use dashu_base::{DivExact, DivExactAssign};
let a = IBig::from(10u8).pow(8) * 7u8;
assert_eq!(a.clone().div_exact(IBig::from(10u8).pow(8), &()), Some(IBig::from(7u8)));
assert_eq!(a.div_exact(IBig::from(3u8), &()), None);
assert_eq!(IBig::from(-14i32).div_exact(IBig::from(7i32), &()), Some(IBig::from(-2i32)));
assert_eq!(IBig::from(14i32).div_exact(IBig::from(-7i32), &()), Some(IBig::from(-2i32)));
assert_eq!(IBig::from(-14i32).div_exact(IBig::from(-7i32), &()), Some(IBig::from(2i32)));
let ref_a = IBig::from(10u8).pow(8) * 7u8;
assert_eq!((&ref_a).div_exact(IBig::from(10u8).pow(8), &()), Some(IBig::from(7u8)));
assert_eq!((&ref_a).div_exact(IBig::from(3u8), &()), None);
assert_eq!(ref_a, IBig::from(10u8).pow(8) * 7u8);
assert_eq!(IBig::from(10u8).pow(8).div_exact(10u8, &()), Some(IBig::from(10u8).pow(7)));
assert_eq!(IBig::from(-20i32).div_exact(5i32, &()), Some(IBig::from(-4i32)));
assert_eq!(IBig::from(20i32).div_exact(-5i32, &()), Some(IBig::from(-4i32)));
assert_eq!(IBig::from(20i32).div_exact(7i32, &()), None);
let mut b = IBig::from(10u8).pow(8) * 7u8;
assert!(b.div_exact_assign(IBig::from(10u8).pow(8), &()));
assert_eq!(b, IBig::from(7u8));
assert!(!b.div_exact_assign(3u8, &())); assert_eq!(b, IBig::from(7u8));
assert!(b.div_exact_assign(-7i32, &()));
assert_eq!(b, IBig::from(-1i32));
}
#[test]
fn test_is_multiple_of_matches_rem() {
for d in [2u16, 3, 5, 7, 10, 12, 16, 25, 255] {
let d = d as Word;
for i in 1..10usize {
for rest in [1u8, 5, 7, 11] {
let n = UBig::from(d).pow(i) * rest;
let want = (&n % UBig::from_word(d)).is_zero();
assert_eq!(
n.is_multiple_of(&UBig::from_word(d)),
want,
"d={d} i={i} rest={rest}"
);
}
}
}
for d in [
(UBig::ONE << 64) + 3u8,
(UBig::ONE << 70) * 5u8,
UBig::ONE << 100,
] {
for i in 1..8usize {
let n = d.clone().pow(i) * 7u8;
let want = (&n % &d).is_zero();
assert_eq!(n.is_multiple_of(&d), want, "d={d:?} i={i}");
}
let n = d.clone().pow(2) + 1u8;
assert!(!n.is_multiple_of(&d));
}
let d = (UBig::ONE << 200) + 1u8;
for i in 1..6usize {
let n = d.clone().pow(i) * 11u8;
let want = (&n % &d).is_zero();
assert_eq!(n.is_multiple_of(&d), want, "i={i}");
}
assert!(!(d.clone().pow(2) + 2u8).is_multiple_of(&d));
}
#[test]
fn test_is_multiple_of_const_matches_rem() {
for (n, d) in [
(UBig::from(24u8), 6u8),
(UBig::from(24u8), 7u8),
(UBig::from(10u8).pow(8), 10u8),
(UBig::from(10u8).pow(8), 3u8),
] {
assert_eq!(
n.is_multiple_of_const(d as DoubleWord),
(&n % UBig::from_word(d as Word)).is_zero()
);
}
}
}