use super::HeaplessBigInt;
use crate::MachineWord;
use const_num_traits::{CarryingMul, CheckedAdd, CheckedDiv, CheckedRem, DivCeil, Nct, One, Zero};
fn div_rem_impl<T, const CAP: usize>(
dividend: &HeaplessBigInt<T, CAP, Nct>,
divisor: &HeaplessBigInt<T, CAP, Nct>,
) -> (HeaplessBigInt<T, CAP, Nct>, HeaplessBigInt<T, CAP, Nct>)
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
use core::cmp::Ordering;
assert!(
!<HeaplessBigInt<T, CAP, Nct> as Zero>::is_zero(divisor),
"HeaplessBigInt: divide by zero"
);
let work_len = core::cmp::max(dividend.len(), divisor.len());
match dividend.cmp(divisor) {
Ordering::Less => {
return (
<HeaplessBigInt<T, CAP, Nct> as Zero>::zero().widened(work_len),
dividend.widened(work_len),
);
}
Ordering::Equal => {
return (
<HeaplessBigInt<T, CAP, Nct> as One>::one().widened(work_len),
<HeaplessBigInt<T, CAP, Nct> as Zero>::zero().widened(work_len),
);
}
Ordering::Greater => {}
}
let d_bits = dividend.bit_length();
let dv_bits = divisor.bit_length();
let mut shift = d_bits - dv_bits;
let mut rem = dividend.widened(work_len);
let wide_divisor = divisor.widened(work_len);
let one = <HeaplessBigInt<T, CAP, Nct> as One>::one().widened(work_len);
let mut quotient = <HeaplessBigInt<T, CAP, Nct> as Zero>::zero().widened(work_len);
let mut shifted = wide_divisor << shift;
let mut bit = one << shift;
loop {
if rem >= shifted {
rem = rem.wrapping_sub(&shifted);
quotient = quotient.wrapping_add(&bit);
}
if shift == 0 {
break;
}
shifted >>= 1usize;
bit >>= 1usize;
shift -= 1;
}
(quotient, rem)
}
macro_rules! div_impls {
($lhs:ty, $rhs:ty, $out:ty) => {
impl<T, const CAP: usize> core::ops::Div<$rhs> for $lhs
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = $out;
fn div(self, other: $rhs) -> Self::Output {
div_rem_impl::<T, CAP>(&self, &other).0
}
}
impl<T, const CAP: usize> core::ops::Rem<$rhs> for $lhs
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = $out;
fn rem(self, other: $rhs) -> Self::Output {
div_rem_impl::<T, CAP>(&self, &other).1
}
}
};
}
div_impls!(
HeaplessBigInt<T, CAP, Nct>,
HeaplessBigInt<T, CAP, Nct>,
HeaplessBigInt<T, CAP, Nct>
);
div_impls!(
HeaplessBigInt<T, CAP, Nct>,
&HeaplessBigInt<T, CAP, Nct>,
HeaplessBigInt<T, CAP, Nct>
);
div_impls!(
&HeaplessBigInt<T, CAP, Nct>,
HeaplessBigInt<T, CAP, Nct>,
HeaplessBigInt<T, CAP, Nct>
);
div_impls!(
&HeaplessBigInt<T, CAP, Nct>,
&HeaplessBigInt<T, CAP, Nct>,
HeaplessBigInt<T, CAP, Nct>
);
impl<T, const CAP: usize> HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
pub fn div_rem(&self, other: &Self) -> (Self, Self) {
div_rem_impl(self, other)
}
pub fn checked_div(&self, other: &Self) -> Option<Self> {
if <Self as Zero>::is_zero(other) {
None
} else {
Some(div_rem_impl(self, other).0)
}
}
pub fn checked_rem(&self, other: &Self) -> Option<Self> {
if <Self as Zero>::is_zero(other) {
None
} else {
Some(div_rem_impl(self, other).1)
}
}
pub fn checked_div_ceil(&self, other: &Self) -> Option<Self> {
if <Self as Zero>::is_zero(other) {
return None;
}
let (q, r) = div_rem_impl(self, other);
if <Self as Zero>::is_zero(&r) {
Some(q)
} else {
CheckedAdd::checked_add(q, <Self as One>::one())
}
}
}
impl<T, const CAP: usize> CheckedDiv for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = Self;
fn checked_div(self, v: Self) -> Option<Self> {
Self::checked_div(&self, &v)
}
}
impl<T, const CAP: usize> CheckedRem for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = Self;
fn checked_rem(self, v: Self) -> Option<Self> {
Self::checked_rem(&self, &v)
}
}
impl<T, const CAP: usize> DivCeil for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = Self;
fn div_ceil(self, rhs: Self) -> Self {
match Self::checked_div_ceil(&self, &rhs) {
Some(v) => v,
None => panic!("HeaplessBigInt::div_ceil: division by zero or overflow"),
}
}
}
impl<T, const CAP: usize> CheckedDiv for &HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = HeaplessBigInt<T, CAP, Nct>;
fn checked_div(self, v: Self) -> Option<Self::Output> {
<HeaplessBigInt<T, CAP, Nct> as CheckedDiv>::checked_div(*self, *v)
}
}
impl<T, const CAP: usize> CheckedRem for &HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = HeaplessBigInt<T, CAP, Nct>;
fn checked_rem(self, v: Self) -> Option<Self::Output> {
<HeaplessBigInt<T, CAP, Nct> as CheckedRem>::checked_rem(*self, *v)
}
}
impl<T, const CAP: usize> DivCeil for &HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
type Output = HeaplessBigInt<T, CAP, Nct>;
fn div_ceil(self, rhs: Self) -> Self::Output {
<HeaplessBigInt<T, CAP, Nct> as DivCeil>::div_ceil(*self, *rhs)
}
}
#[cfg(feature = "num-traits")]
impl<T, const CAP: usize> num_traits::CheckedDiv for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
fn checked_div(&self, v: &Self) -> Option<Self> {
Self::checked_div(self, v)
}
}
#[cfg(feature = "num-traits")]
impl<T, const CAP: usize> num_traits::CheckedRem for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
fn checked_rem(&self, v: &Self) -> Option<Self> {
Self::checked_rem(self, v)
}
}
impl<T, const CAP: usize> core::ops::DivAssign for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
fn div_assign(&mut self, other: Self) {
*self = div_rem_impl::<T, CAP>(self, &other).0;
}
}
impl<T, const CAP: usize> core::ops::DivAssign<&HeaplessBigInt<T, CAP, Nct>>
for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
fn div_assign(&mut self, other: &Self) {
*self = div_rem_impl::<T, CAP>(self, other).0;
}
}
impl<T, const CAP: usize> core::ops::RemAssign for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
fn rem_assign(&mut self, other: Self) {
*self = div_rem_impl::<T, CAP>(self, &other).1;
}
}
impl<T, const CAP: usize> core::ops::RemAssign<&HeaplessBigInt<T, CAP, Nct>>
for HeaplessBigInt<T, CAP, Nct>
where
T: MachineWord + CarryingMul<Unsigned = T, Output = T>,
{
fn rem_assign(&mut self, other: &Self) {
*self = div_rem_impl::<T, CAP>(self, other).1;
}
}
#[cfg(test)]
mod div_ceil_tests {
use super::HeaplessBigInt;
use const_num_traits::{CheckedDiv, CheckedRem, DivCeil};
type H = HeaplessBigInt<u8, 4>;
#[test]
fn div_ceil_rounds_up() {
assert_eq!(DivCeil::div_ceil(H::from(10u8), H::from(5u8)), H::from(2u8));
assert_eq!(DivCeil::div_ceil(H::from(11u8), H::from(3u8)), H::from(4u8));
assert_eq!(DivCeil::div_ceil(H::from(1u8), H::from(5u8)), H::from(1u8));
assert_eq!(DivCeil::div_ceil(H::from(0u8), H::from(5u8)), H::from(0u8));
}
#[test]
fn checked_div_ceil_edges() {
assert_eq!(H::from(10u8).checked_div_ceil(&H::from(0u8)), None);
assert_eq!(
H::from(u32::MAX).checked_div_ceil(&H::from(2u8)),
Some(H::from(0x8000_0000u32))
);
}
#[test]
fn by_ref_matches_value() {
let a = H::from(100u8);
let b = H::from(7u8);
assert_eq!(
CheckedDiv::checked_div(&a, &b),
CheckedDiv::checked_div(a, b)
);
assert_eq!(
CheckedRem::checked_rem(&a, &b),
CheckedRem::checked_rem(a, b)
);
assert_eq!(DivCeil::div_ceil(&a, &b), DivCeil::div_ceil(a, b));
}
}