use super::div_limb::{div_rem_limb_with_reciprocal, rem_limb_with_reciprocal, Reciprocal};
use crate::{CheckedDiv, ConstChoice, DivRemLimb, Limb, NonZero, RemLimb, Uint, Word, Wrapping};
use core::ops::{Div, DivAssign, Rem, RemAssign};
use subtle::CtOption;
impl<const LIMBS: usize> Uint<LIMBS> {
#[inline(always)]
pub const fn div_rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> (Self, Limb) {
div_rem_limb_with_reciprocal(self, reciprocal)
}
#[inline(always)]
pub const fn div_rem_limb(&self, rhs: NonZero<Limb>) -> (Self, Limb) {
div_rem_limb_with_reciprocal(self, &Reciprocal::new(rhs))
}
#[inline(always)]
pub const fn rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> Limb {
rem_limb_with_reciprocal(self, reciprocal)
}
#[inline(always)]
pub const fn rem_limb(&self, rhs: NonZero<Limb>) -> Limb {
rem_limb_with_reciprocal(self, &Reciprocal::new(rhs))
}
#[allow(trivial_numeric_casts)]
pub const fn div_rem(&self, rhs: &NonZero<Self>) -> (Self, Self) {
let mb = rhs.0.bits();
let mut rem = *self;
let mut quo = Self::ZERO;
let mut c = rhs.0.wrapping_shl(Self::BITS - mb);
let mut i = Self::BITS;
let mut done = ConstChoice::FALSE;
loop {
let (mut r, borrow) = rem.sbb(&c, Limb::ZERO);
rem = Self::select(&r, &rem, ConstChoice::from_word_mask(borrow.0).or(done));
r = quo.bitor(&Self::ONE);
quo = Self::select(&r, &quo, ConstChoice::from_word_mask(borrow.0).or(done));
if i == 0 {
break;
}
i -= 1;
done = ConstChoice::from_word_lt(i as Word, mb as Word);
c = c.shr1();
quo = Self::select(&quo.shl1(), &quo, done);
}
(quo, rem)
}
#[allow(trivial_numeric_casts)]
pub const fn div_rem_vartime(&self, rhs: &NonZero<Self>) -> (Self, Self) {
let mb = rhs.0.bits_vartime();
let mut bd = Self::BITS - mb;
let mut rem = *self;
let mut quo = Self::ZERO;
let mut c = rhs.0.wrapping_shl_vartime(bd);
loop {
let (mut r, borrow) = rem.sbb(&c, Limb::ZERO);
rem = Self::select(&r, &rem, ConstChoice::from_word_mask(borrow.0));
r = quo.bitor(&Self::ONE);
quo = Self::select(&r, &quo, ConstChoice::from_word_mask(borrow.0));
if bd == 0 {
break;
}
bd -= 1;
c = c.shr1();
quo = quo.shl1();
}
(quo, rem)
}
pub const fn rem(&self, rhs: &NonZero<Self>) -> Self {
self.div_rem(rhs).1
}
pub const fn rem_vartime(&self, rhs: &NonZero<Self>) -> Self {
let mb = rhs.0.bits_vartime();
let mut bd = Self::BITS - mb;
let mut rem = *self;
let mut c = rhs.0.wrapping_shl_vartime(bd);
loop {
let (r, borrow) = rem.sbb(&c, Limb::ZERO);
rem = Self::select(&r, &rem, ConstChoice::from_word_mask(borrow.0));
if bd == 0 {
break;
}
bd -= 1;
c = c.shr1();
}
rem
}
pub const fn rem_wide_vartime(lower_upper: (Self, Self), rhs: &NonZero<Self>) -> Self {
let mb = rhs.0.bits_vartime();
let mut bd = (2 * Self::BITS) - mb;
let (mut lower, mut upper) = lower_upper;
let mut c = Self::overflowing_shl_vartime_wide((rhs.0, Uint::ZERO), bd)
.expect("shift within range");
loop {
let (lower_sub, borrow) = lower.sbb(&c.0, Limb::ZERO);
let (upper_sub, borrow) = upper.sbb(&c.1, borrow);
lower = Self::select(&lower_sub, &lower, ConstChoice::from_word_mask(borrow.0));
upper = Self::select(&upper_sub, &upper, ConstChoice::from_word_mask(borrow.0));
if bd == 0 {
break;
}
bd -= 1;
c = Self::overflowing_shr_vartime_wide(c, 1).expect("shift within range");
}
lower
}
pub const fn rem2k(&self, k: u32) -> Self {
let highest = (LIMBS - 1) as u32;
let index = k / Limb::BITS;
let le = ConstChoice::from_u32_le(index, highest);
let limb_num = le.select_u32(highest, index) as usize;
let base = k % Limb::BITS;
let mask = (1 << base) - 1;
let mut out = *self;
let outmask = Limb(out.limbs[limb_num].0 & mask);
out.limbs[limb_num] = Limb::select(out.limbs[limb_num], outmask, le);
let mut i = limb_num + 1;
while i < LIMBS {
out.limbs[i] = Limb::ZERO;
i += 1;
}
out
}
pub const fn wrapping_div(&self, rhs: &NonZero<Self>) -> Self {
let (q, _) = self.div_rem(rhs);
q
}
pub const fn wrapping_div_vartime(&self, rhs: &NonZero<Self>) -> Self {
let (q, _) = self.div_rem_vartime(rhs);
q
}
pub fn checked_div(&self, rhs: &Self) -> CtOption<Self> {
NonZero::new(*rhs).map(|rhs| {
let (q, _r) = self.div_rem(&rhs);
q
})
}
pub const fn wrapping_rem(&self, rhs: &Self) -> Self {
let nz_rhs = rhs.to_nz().expect("non-zero divisor");
self.rem_vartime(&nz_rhs)
}
pub fn checked_rem(&self, rhs: &Self) -> CtOption<Self> {
NonZero::new(*rhs).map(|rhs| self.rem(&rhs))
}
}
impl<const LIMBS: usize> Div<&NonZero<Limb>> for &Uint<LIMBS> {
type Output = Uint<LIMBS>;
fn div(self, rhs: &NonZero<Limb>) -> Self::Output {
*self / *rhs
}
}
impl<const LIMBS: usize> Div<&NonZero<Limb>> for Uint<LIMBS> {
type Output = Uint<LIMBS>;
fn div(self, rhs: &NonZero<Limb>) -> Self::Output {
self / *rhs
}
}
impl<const LIMBS: usize> Div<NonZero<Limb>> for &Uint<LIMBS> {
type Output = Uint<LIMBS>;
fn div(self, rhs: NonZero<Limb>) -> Self::Output {
*self / rhs
}
}
impl<const LIMBS: usize> Div<NonZero<Limb>> for Uint<LIMBS> {
type Output = Uint<LIMBS>;
fn div(self, rhs: NonZero<Limb>) -> Self::Output {
let (q, _) = self.div_rem_limb(rhs);
q
}
}
impl<const LIMBS: usize> DivAssign<&NonZero<Limb>> for Uint<LIMBS> {
fn div_assign(&mut self, rhs: &NonZero<Limb>) {
*self /= *rhs;
}
}
impl<const LIMBS: usize> DivAssign<NonZero<Limb>> for Uint<LIMBS> {
fn div_assign(&mut self, rhs: NonZero<Limb>) {
*self = *self / rhs;
}
}
impl<const LIMBS: usize> Div<NonZero<Limb>> for Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Uint<LIMBS>>;
fn div(self, rhs: NonZero<Limb>) -> Self::Output {
Wrapping(self.0 / rhs)
}
}
impl<const LIMBS: usize> Div<NonZero<Limb>> for &Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Uint<LIMBS>>;
fn div(self, rhs: NonZero<Limb>) -> Self::Output {
*self / rhs
}
}
impl<const LIMBS: usize> Div<&NonZero<Limb>> for &Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Uint<LIMBS>>;
fn div(self, rhs: &NonZero<Limb>) -> Self::Output {
*self / *rhs
}
}
impl<const LIMBS: usize> Div<&NonZero<Limb>> for Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Uint<LIMBS>>;
fn div(self, rhs: &NonZero<Limb>) -> Self::Output {
self / *rhs
}
}
impl<const LIMBS: usize> DivAssign<&NonZero<Limb>> for Wrapping<Uint<LIMBS>> {
fn div_assign(&mut self, rhs: &NonZero<Limb>) {
*self = Wrapping(self.0 / rhs)
}
}
impl<const LIMBS: usize> DivAssign<NonZero<Limb>> for Wrapping<Uint<LIMBS>> {
fn div_assign(&mut self, rhs: NonZero<Limb>) {
*self /= &rhs;
}
}
impl<const LIMBS: usize> Rem<&NonZero<Limb>> for &Uint<LIMBS> {
type Output = Limb;
fn rem(self, rhs: &NonZero<Limb>) -> Self::Output {
*self % *rhs
}
}
impl<const LIMBS: usize> Rem<&NonZero<Limb>> for Uint<LIMBS> {
type Output = Limb;
fn rem(self, rhs: &NonZero<Limb>) -> Self::Output {
self % *rhs
}
}
impl<const LIMBS: usize> Rem<NonZero<Limb>> for &Uint<LIMBS> {
type Output = Limb;
fn rem(self, rhs: NonZero<Limb>) -> Self::Output {
*self % rhs
}
}
impl<const LIMBS: usize> Rem<NonZero<Limb>> for Uint<LIMBS> {
type Output = Limb;
fn rem(self, rhs: NonZero<Limb>) -> Self::Output {
let (_, r) = self.div_rem_limb(rhs);
r
}
}
impl<const LIMBS: usize> RemAssign<&NonZero<Limb>> for Uint<LIMBS> {
fn rem_assign(&mut self, rhs: &NonZero<Limb>) {
*self = (*self % rhs).into();
}
}
impl<const LIMBS: usize> RemAssign<NonZero<Limb>> for Uint<LIMBS> {
fn rem_assign(&mut self, rhs: NonZero<Limb>) {
*self %= &rhs;
}
}
impl<const LIMBS: usize> Rem<NonZero<Limb>> for Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Limb>;
fn rem(self, rhs: NonZero<Limb>) -> Self::Output {
Wrapping(self.0 % rhs)
}
}
impl<const LIMBS: usize> Rem<NonZero<Limb>> for &Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Limb>;
fn rem(self, rhs: NonZero<Limb>) -> Self::Output {
*self % rhs
}
}
impl<const LIMBS: usize> Rem<&NonZero<Limb>> for &Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Limb>;
fn rem(self, rhs: &NonZero<Limb>) -> Self::Output {
*self % *rhs
}
}
impl<const LIMBS: usize> Rem<&NonZero<Limb>> for Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Limb>;
fn rem(self, rhs: &NonZero<Limb>) -> Self::Output {
self % *rhs
}
}
impl<const LIMBS: usize> RemAssign<NonZero<Limb>> for Wrapping<Uint<LIMBS>> {
fn rem_assign(&mut self, rhs: NonZero<Limb>) {
*self %= &rhs;
}
}
impl<const LIMBS: usize> RemAssign<&NonZero<Limb>> for Wrapping<Uint<LIMBS>> {
fn rem_assign(&mut self, rhs: &NonZero<Limb>) {
*self = Wrapping((self.0 % rhs).into())
}
}
impl<const LIMBS: usize> CheckedDiv for Uint<LIMBS> {
fn checked_div(&self, rhs: &Uint<LIMBS>) -> CtOption<Self> {
self.checked_div(rhs)
}
}
impl<const LIMBS: usize> Div<&NonZero<Uint<LIMBS>>> for &Uint<LIMBS> {
type Output = Uint<LIMBS>;
fn div(self, rhs: &NonZero<Uint<LIMBS>>) -> Self::Output {
*self / *rhs
}
}
impl<const LIMBS: usize> Div<&NonZero<Uint<LIMBS>>> for Uint<LIMBS> {
type Output = Uint<LIMBS>;
fn div(self, rhs: &NonZero<Uint<LIMBS>>) -> Self::Output {
self / *rhs
}
}
impl<const LIMBS: usize> Div<NonZero<Uint<LIMBS>>> for &Uint<LIMBS> {
type Output = Uint<LIMBS>;
fn div(self, rhs: NonZero<Uint<LIMBS>>) -> Self::Output {
*self / rhs
}
}
impl<const LIMBS: usize> Div<NonZero<Uint<LIMBS>>> for Uint<LIMBS> {
type Output = Uint<LIMBS>;
fn div(self, rhs: NonZero<Uint<LIMBS>>) -> Self::Output {
let (q, _) = self.div_rem(&rhs);
q
}
}
impl<const LIMBS: usize> DivAssign<&NonZero<Uint<LIMBS>>> for Uint<LIMBS> {
fn div_assign(&mut self, rhs: &NonZero<Uint<LIMBS>>) {
*self /= *rhs
}
}
impl<const LIMBS: usize> DivAssign<NonZero<Uint<LIMBS>>> for Uint<LIMBS> {
fn div_assign(&mut self, rhs: NonZero<Uint<LIMBS>>) {
*self = *self / rhs;
}
}
impl<const LIMBS: usize> Div<NonZero<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Uint<LIMBS>>;
fn div(self, rhs: NonZero<Uint<LIMBS>>) -> Self::Output {
Wrapping(self.0 / rhs)
}
}
impl<const LIMBS: usize> Div<NonZero<Uint<LIMBS>>> for &Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Uint<LIMBS>>;
fn div(self, rhs: NonZero<Uint<LIMBS>>) -> Self::Output {
*self / rhs
}
}
impl<const LIMBS: usize> Div<&NonZero<Uint<LIMBS>>> for &Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Uint<LIMBS>>;
fn div(self, rhs: &NonZero<Uint<LIMBS>>) -> Self::Output {
*self / *rhs
}
}
impl<const LIMBS: usize> Div<&NonZero<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Uint<LIMBS>>;
fn div(self, rhs: &NonZero<Uint<LIMBS>>) -> Self::Output {
self / *rhs
}
}
impl<const LIMBS: usize> DivAssign<&NonZero<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
fn div_assign(&mut self, rhs: &NonZero<Uint<LIMBS>>) {
*self = Wrapping(self.0 / rhs);
}
}
impl<const LIMBS: usize> DivAssign<NonZero<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
fn div_assign(&mut self, rhs: NonZero<Uint<LIMBS>>) {
*self /= &rhs;
}
}
impl<const LIMBS: usize> Rem<&NonZero<Uint<LIMBS>>> for &Uint<LIMBS> {
type Output = Uint<LIMBS>;
fn rem(self, rhs: &NonZero<Uint<LIMBS>>) -> Self::Output {
*self % *rhs
}
}
impl<const LIMBS: usize> Rem<&NonZero<Uint<LIMBS>>> for Uint<LIMBS> {
type Output = Uint<LIMBS>;
fn rem(self, rhs: &NonZero<Uint<LIMBS>>) -> Self::Output {
self % *rhs
}
}
impl<const LIMBS: usize> Rem<NonZero<Uint<LIMBS>>> for &Uint<LIMBS> {
type Output = Uint<LIMBS>;
fn rem(self, rhs: NonZero<Uint<LIMBS>>) -> Self::Output {
*self % rhs
}
}
impl<const LIMBS: usize> Rem<NonZero<Uint<LIMBS>>> for Uint<LIMBS> {
type Output = Uint<LIMBS>;
fn rem(self, rhs: NonZero<Uint<LIMBS>>) -> Self::Output {
Self::rem_vartime(&self, &rhs)
}
}
impl<const LIMBS: usize> RemAssign<&NonZero<Uint<LIMBS>>> for Uint<LIMBS> {
fn rem_assign(&mut self, rhs: &NonZero<Uint<LIMBS>>) {
*self %= *rhs
}
}
impl<const LIMBS: usize> RemAssign<NonZero<Uint<LIMBS>>> for Uint<LIMBS> {
fn rem_assign(&mut self, rhs: NonZero<Uint<LIMBS>>) {
*self = *self % rhs;
}
}
impl<const LIMBS: usize> Rem<NonZero<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Uint<LIMBS>>;
fn rem(self, rhs: NonZero<Uint<LIMBS>>) -> Self::Output {
Wrapping(self.0 % rhs)
}
}
impl<const LIMBS: usize> Rem<NonZero<Uint<LIMBS>>> for &Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Uint<LIMBS>>;
fn rem(self, rhs: NonZero<Uint<LIMBS>>) -> Self::Output {
*self % rhs
}
}
impl<const LIMBS: usize> Rem<&NonZero<Uint<LIMBS>>> for &Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Uint<LIMBS>>;
fn rem(self, rhs: &NonZero<Uint<LIMBS>>) -> Self::Output {
*self % *rhs
}
}
impl<const LIMBS: usize> Rem<&NonZero<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
type Output = Wrapping<Uint<LIMBS>>;
fn rem(self, rhs: &NonZero<Uint<LIMBS>>) -> Self::Output {
self % *rhs
}
}
impl<const LIMBS: usize> RemAssign<NonZero<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
fn rem_assign(&mut self, rhs: NonZero<Uint<LIMBS>>) {
*self %= &rhs;
}
}
impl<const LIMBS: usize> RemAssign<&NonZero<Uint<LIMBS>>> for Wrapping<Uint<LIMBS>> {
fn rem_assign(&mut self, rhs: &NonZero<Uint<LIMBS>>) {
*self = Wrapping(self.0 % rhs)
}
}
impl<const LIMBS: usize> DivRemLimb for Uint<LIMBS> {
fn div_rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> (Self, Limb) {
Self::div_rem_limb_with_reciprocal(self, reciprocal)
}
}
impl<const LIMBS: usize> RemLimb for Uint<LIMBS> {
fn rem_limb_with_reciprocal(&self, reciprocal: &Reciprocal) -> Limb {
Self::rem_limb_with_reciprocal(self, reciprocal)
}
}
#[cfg(test)]
mod tests {
use crate::{Limb, NonZero, Uint, Word, U256};
#[cfg(feature = "rand")]
use {
crate::{CheckedMul, Random},
rand_chacha::ChaChaRng,
rand_core::RngCore,
rand_core::SeedableRng,
};
#[test]
fn div_word() {
for (n, d, e, ee) in &[
(200u64, 2u64, 100u64, 0),
(100u64, 25u64, 4u64, 0),
(100u64, 10u64, 10u64, 0),
(1024u64, 8u64, 128u64, 0),
(27u64, 13u64, 2u64, 1u64),
(26u64, 13u64, 2u64, 0u64),
(14u64, 13u64, 1u64, 1u64),
(13u64, 13u64, 1u64, 0u64),
(12u64, 13u64, 0u64, 12u64),
(1u64, 13u64, 0u64, 1u64),
] {
let lhs = U256::from(*n);
let rhs = NonZero::new(U256::from(*d)).unwrap();
let (q, r) = lhs.div_rem(&rhs);
assert_eq!(U256::from(*e), q);
assert_eq!(U256::from(*ee), r);
let (q, r) = lhs.div_rem_vartime(&rhs);
assert_eq!(U256::from(*e), q);
assert_eq!(U256::from(*ee), r);
}
}
#[cfg(feature = "rand")]
#[test]
fn div() {
let mut rng = ChaChaRng::from_seed([7u8; 32]);
for _ in 0..25 {
let num = U256::random(&mut rng).overflowing_shr_vartime(128).unwrap();
let den =
NonZero::new(U256::random(&mut rng).overflowing_shr_vartime(128).unwrap()).unwrap();
let n = num.checked_mul(den.as_ref());
if n.is_some().into() {
let (q, _) = n.unwrap().div_rem(&den);
assert_eq!(q, num);
let (q, _) = n.unwrap().div_rem_vartime(&den);
assert_eq!(q, num);
}
}
}
#[test]
fn div_max() {
let mut a = U256::ZERO;
let mut b = U256::ZERO;
b.limbs[b.limbs.len() - 1] = Limb(Word::MAX);
let q = a.wrapping_div(&NonZero::new(b).unwrap());
assert_eq!(q, Uint::ZERO);
a.limbs[a.limbs.len() - 1] = Limb(1 << (Limb::HI_BIT - 7));
b.limbs[b.limbs.len() - 1] = Limb(0x82 << (Limb::HI_BIT - 7));
let q = a.wrapping_div(&NonZero::new(b).unwrap());
assert_eq!(q, Uint::ZERO);
}
#[test]
fn div_one() {
let (q, r) = U256::from(10u8).div_rem(&NonZero::new(U256::ONE).unwrap());
assert_eq!(q, U256::from(10u8));
assert_eq!(r, U256::ZERO);
let (q, r) = U256::from(10u8).div_rem_vartime(&NonZero::new(U256::ONE).unwrap());
assert_eq!(q, U256::from(10u8));
assert_eq!(r, U256::ZERO);
}
#[test]
fn reduce_one() {
let r = U256::from(10u8).rem_vartime(&NonZero::new(U256::ONE).unwrap());
assert_eq!(r, U256::ZERO);
}
#[test]
fn reduce_tests() {
let r = U256::from(10u8).rem_vartime(&NonZero::new(U256::from(2u8)).unwrap());
assert_eq!(r, U256::ZERO);
let r = U256::from(10u8).rem_vartime(&NonZero::new(U256::from(3u8)).unwrap());
assert_eq!(r, U256::ONE);
let r = U256::from(10u8).rem_vartime(&NonZero::new(U256::from(7u8)).unwrap());
assert_eq!(r, U256::from(3u8));
}
#[test]
fn reduce_tests_wide_zero_padded() {
let r = U256::rem_wide_vartime(
(U256::from(10u8), U256::ZERO),
&NonZero::new(U256::from(2u8)).unwrap(),
);
assert_eq!(r, U256::ZERO);
let r = U256::rem_wide_vartime(
(U256::from(10u8), U256::ZERO),
&NonZero::new(U256::from(3u8)).unwrap(),
);
assert_eq!(r, U256::ONE);
let r = U256::rem_wide_vartime(
(U256::from(10u8), U256::ZERO),
&NonZero::new(U256::from(7u8)).unwrap(),
);
assert_eq!(r, U256::from(3u8));
}
#[test]
fn reduce_max() {
let mut a = U256::ZERO;
let mut b = U256::ZERO;
b.limbs[b.limbs.len() - 1] = Limb(Word::MAX);
let r = a.wrapping_rem(&b);
assert_eq!(r, Uint::ZERO);
a.limbs[a.limbs.len() - 1] = Limb(1 << (Limb::HI_BIT - 7));
b.limbs[b.limbs.len() - 1] = Limb(0x82 << (Limb::HI_BIT - 7));
let r = a.wrapping_rem(&b);
assert_eq!(r, a);
}
#[cfg(feature = "rand")]
#[test]
fn rem2krand() {
let mut rng = ChaChaRng::from_seed([7u8; 32]);
for _ in 0..25 {
let num = U256::random(&mut rng);
let k = rng.next_u32() % 256;
let den = U256::ONE.overflowing_shl_vartime(k).unwrap();
let a = num.rem2k(k);
let e = num.wrapping_rem(&den);
assert_eq!(a, e);
}
}
#[allow(clippy::op_ref)]
#[test]
fn rem_trait() {
let a = U256::from(10u64);
let b = NonZero::new(U256::from(3u64)).unwrap();
let c = U256::from(1u64);
assert_eq!(a % b, c);
assert_eq!(a % &b, c);
assert_eq!(&a % b, c);
assert_eq!(&a % &b, c);
}
}