use crate::{CheckedDiv, Choice, CtOption, DivVartime, Int, NonZero, Uint, Wrapping};
use core::ops::{Div, DivAssign, Rem, RemAssign};
impl<const LIMBS: usize> Int<LIMBS> {
#[inline]
const fn div_rem_base<const RHS_LIMBS: usize>(
&self,
rhs: &NonZero<Int<RHS_LIMBS>>,
) -> (Uint<LIMBS>, Uint<RHS_LIMBS>, Choice, Choice) {
let (lhs_mag, lhs_sgn) = self.abs_sign();
let (rhs_mag, rhs_sgn) = rhs.abs_sign();
let (quotient, remainder) = lhs_mag.div_rem(&rhs_mag);
(quotient, remainder, lhs_sgn, rhs_sgn)
}
#[must_use]
pub const fn checked_div_rem<const RHS_LIMBS: usize>(
&self,
rhs: &NonZero<Int<RHS_LIMBS>>,
) -> (CtOption<Self>, Int<RHS_LIMBS>) {
let (quotient, remainder, lhs_sgn, rhs_sgn) = self.div_rem_base(rhs);
let opposing_signs = lhs_sgn.ne(rhs_sgn);
(
Self::new_from_abs_sign(quotient, opposing_signs),
remainder.as_int().wrapping_neg_if(lhs_sgn), )
}
#[must_use]
pub fn checked_div<const RHS_LIMBS: usize>(&self, rhs: &Int<RHS_LIMBS>) -> CtOption<Self> {
NonZero::new(*rhs).and_then(|rhs| self.checked_div_rem(&rhs).0)
}
#[must_use]
pub const fn rem<const RHS_LIMBS: usize>(
&self,
rhs: &NonZero<Int<RHS_LIMBS>>,
) -> Int<RHS_LIMBS> {
self.checked_div_rem(rhs).1
}
}
impl<const LIMBS: usize> Int<LIMBS> {
#[inline]
const fn div_rem_base_vartime<const RHS_LIMBS: usize>(
&self,
rhs: &NonZero<Int<RHS_LIMBS>>,
) -> (Uint<LIMBS>, Uint<RHS_LIMBS>, Choice, Choice) {
let (lhs_mag, lhs_sgn) = self.abs_sign();
let (rhs_mag, rhs_sgn) = rhs.abs_sign();
let (quotient, remainder) = lhs_mag.div_rem_vartime(&rhs_mag);
(quotient, remainder, lhs_sgn, rhs_sgn)
}
#[must_use]
pub const fn checked_div_rem_vartime<const RHS_LIMBS: usize>(
&self,
rhs: &NonZero<Int<RHS_LIMBS>>,
) -> (CtOption<Self>, Int<RHS_LIMBS>) {
let (quotient, remainder, lhs_sgn, rhs_sgn) = self.div_rem_base_vartime(rhs);
let opposing_signs = lhs_sgn.ne(rhs_sgn);
(
Self::new_from_abs_sign(quotient, opposing_signs),
remainder.as_int().wrapping_neg_if(lhs_sgn), )
}
#[must_use]
pub fn checked_div_vartime<const RHS_LIMBS: usize>(
&self,
rhs: &Int<RHS_LIMBS>,
) -> CtOption<Self> {
NonZero::new(*rhs).and_then(|rhs| self.checked_div_rem_vartime(&rhs).0)
}
#[must_use]
pub const fn rem_vartime<const RHS_LIMBS: usize>(
&self,
rhs: &NonZero<Int<RHS_LIMBS>>,
) -> Int<RHS_LIMBS> {
self.checked_div_rem_vartime(rhs).1
}
}
impl<const LIMBS: usize> Int<LIMBS> {
#[must_use]
pub const fn checked_div_rem_floor_vartime<const RHS_LIMBS: usize>(
&self,
rhs: &NonZero<Int<RHS_LIMBS>>,
) -> (CtOption<Self>, Int<RHS_LIMBS>) {
let (lhs_mag, lhs_sgn) = self.abs_sign();
let (rhs_mag, rhs_sgn) = rhs.abs_sign();
let (quotient, remainder) = lhs_mag.div_rem_vartime(&rhs_mag);
let opposing_signs = lhs_sgn.xor(rhs_sgn);
let modify = remainder.is_nonzero().and(opposing_signs);
let quotient_plus_one = quotient.wrapping_add(&Uint::ONE); let quotient = Uint::select("ient, "ient_plus_one, modify);
let inv_remainder = rhs_mag.0.wrapping_sub(&remainder);
let remainder = Uint::select(&remainder, &inv_remainder, modify);
let quotient = Int::new_from_abs_sign(quotient, opposing_signs);
let remainder = remainder.as_int().wrapping_neg_if(opposing_signs);
(quotient, remainder)
}
#[must_use]
pub fn checked_div_floor_vartime<const RHS_LIMBS: usize>(
&self,
rhs: &Int<RHS_LIMBS>,
) -> CtOption<Self> {
NonZero::new(*rhs).and_then(|rhs| self.checked_div_rem_floor_vartime(&rhs).0)
}
}
impl<const LIMBS: usize> Int<LIMBS> {
#[must_use]
pub fn checked_div_floor<const RHS_LIMBS: usize>(
&self,
rhs: &Int<RHS_LIMBS>,
) -> CtOption<Self> {
NonZero::new(*rhs).and_then(|rhs| self.checked_div_rem_floor(&rhs).0)
}
#[must_use]
pub const fn checked_div_rem_floor<const RHS_LIMBS: usize>(
&self,
rhs: &NonZero<Int<RHS_LIMBS>>,
) -> (CtOption<Self>, Int<RHS_LIMBS>) {
let (lhs_mag, lhs_sgn) = self.abs_sign();
let (rhs_mag, rhs_sgn) = rhs.abs_sign();
let (quotient, remainder) = lhs_mag.div_rem(&rhs_mag);
let opposing_signs = lhs_sgn.xor(rhs_sgn);
let modify = remainder.is_nonzero().and(opposing_signs);
let quotient_plus_one = quotient.wrapping_add(&Uint::ONE); let quotient = Uint::select("ient, "ient_plus_one, modify);
let inv_remainder = rhs_mag.0.wrapping_sub(&remainder);
let remainder = Uint::select(&remainder, &inv_remainder, modify);
let quotient = Int::new_from_abs_sign(quotient, opposing_signs);
let remainder = remainder.as_int().wrapping_neg_if(opposing_signs);
(quotient, remainder)
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> CheckedDiv<Int<RHS_LIMBS>> for Int<LIMBS> {
fn checked_div(&self, rhs: &Int<RHS_LIMBS>) -> CtOption<Self> {
self.checked_div(rhs)
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Div<&NonZero<Int<RHS_LIMBS>>> for &Int<LIMBS> {
type Output = CtOption<Int<LIMBS>>;
fn div(self, rhs: &NonZero<Int<RHS_LIMBS>>) -> Self::Output {
*self / *rhs
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Div<&NonZero<Int<RHS_LIMBS>>> for Int<LIMBS> {
type Output = CtOption<Int<LIMBS>>;
fn div(self, rhs: &NonZero<Int<RHS_LIMBS>>) -> Self::Output {
self / *rhs
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Div<NonZero<Int<RHS_LIMBS>>> for &Int<LIMBS> {
type Output = CtOption<Int<LIMBS>>;
fn div(self, rhs: NonZero<Int<RHS_LIMBS>>) -> Self::Output {
*self / rhs
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Div<NonZero<Int<RHS_LIMBS>>> for Int<LIMBS> {
type Output = CtOption<Int<LIMBS>>;
fn div(self, rhs: NonZero<Int<RHS_LIMBS>>) -> Self::Output {
self.checked_div(&rhs)
}
}
impl<const LIMBS: usize> DivAssign<&NonZero<Int<LIMBS>>> for Int<LIMBS> {
fn div_assign(&mut self, rhs: &NonZero<Int<LIMBS>>) {
*self /= *rhs;
}
}
impl<const LIMBS: usize> DivAssign<NonZero<Int<LIMBS>>> for Int<LIMBS> {
fn div_assign(&mut self, rhs: NonZero<Int<LIMBS>>) {
*self = (*self / rhs).expect("cannot represent positive equivalent of Int::MIN as int");
}
}
impl<const LIMBS: usize> DivVartime for Int<LIMBS> {
fn div_vartime(&self, rhs: &NonZero<Int<LIMBS>>) -> Self {
let (q, _r, lhs_sign, rhs_sign) = self.div_rem_base_vartime(rhs);
let opposing_signs = lhs_sign.xor(rhs_sign);
let q = Int::new_from_abs_sign(q, opposing_signs);
q.expect("int divided by int fits in uint by construction")
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Div<NonZero<Int<RHS_LIMBS>>>
for Wrapping<Int<LIMBS>>
{
type Output = Wrapping<Int<LIMBS>>;
fn div(self, rhs: NonZero<Int<RHS_LIMBS>>) -> Self::Output {
Wrapping((self.0 / rhs).expect("cannot represent positive equivalent of Int::MIN as int"))
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Div<NonZero<Int<RHS_LIMBS>>>
for &Wrapping<Int<LIMBS>>
{
type Output = Wrapping<Int<LIMBS>>;
fn div(self, rhs: NonZero<Int<RHS_LIMBS>>) -> Self::Output {
*self / rhs
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Div<&NonZero<Int<RHS_LIMBS>>>
for &Wrapping<Int<LIMBS>>
{
type Output = Wrapping<Int<LIMBS>>;
fn div(self, rhs: &NonZero<Int<RHS_LIMBS>>) -> Self::Output {
*self / *rhs
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Div<&NonZero<Int<RHS_LIMBS>>>
for Wrapping<Int<LIMBS>>
{
type Output = Wrapping<Int<LIMBS>>;
fn div(self, rhs: &NonZero<Int<RHS_LIMBS>>) -> Self::Output {
self / *rhs
}
}
impl<const LIMBS: usize> DivAssign<&NonZero<Int<LIMBS>>> for Wrapping<Int<LIMBS>> {
fn div_assign(&mut self, rhs: &NonZero<Int<LIMBS>>) {
*self = Wrapping(
(self.0 / rhs).expect("cannot represent positive equivalent of Int::MIN as int"),
);
}
}
impl<const LIMBS: usize> DivAssign<NonZero<Int<LIMBS>>> for Wrapping<Int<LIMBS>> {
fn div_assign(&mut self, rhs: NonZero<Int<LIMBS>>) {
*self /= &rhs;
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Rem<&NonZero<Int<RHS_LIMBS>>> for &Int<LIMBS> {
type Output = Int<RHS_LIMBS>;
fn rem(self, rhs: &NonZero<Int<RHS_LIMBS>>) -> Self::Output {
*self % *rhs
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Rem<&NonZero<Int<RHS_LIMBS>>> for Int<LIMBS> {
type Output = Int<RHS_LIMBS>;
fn rem(self, rhs: &NonZero<Int<RHS_LIMBS>>) -> Self::Output {
self % *rhs
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Rem<NonZero<Int<RHS_LIMBS>>> for &Int<LIMBS> {
type Output = Int<RHS_LIMBS>;
fn rem(self, rhs: NonZero<Int<RHS_LIMBS>>) -> Self::Output {
*self % rhs
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Rem<NonZero<Int<RHS_LIMBS>>> for Int<LIMBS> {
type Output = Int<RHS_LIMBS>;
fn rem(self, rhs: NonZero<Int<RHS_LIMBS>>) -> Self::Output {
Self::rem(&self, &rhs)
}
}
impl<const LIMBS: usize> RemAssign<&NonZero<Int<LIMBS>>> for Int<LIMBS> {
fn rem_assign(&mut self, rhs: &NonZero<Int<LIMBS>>) {
*self %= *rhs;
}
}
impl<const LIMBS: usize> RemAssign<NonZero<Int<LIMBS>>> for Int<LIMBS> {
fn rem_assign(&mut self, rhs: NonZero<Int<LIMBS>>) {
*self = *self % rhs;
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Rem<NonZero<Int<RHS_LIMBS>>>
for Wrapping<Int<LIMBS>>
{
type Output = Wrapping<Int<RHS_LIMBS>>;
fn rem(self, rhs: NonZero<Int<RHS_LIMBS>>) -> Self::Output {
Wrapping(self.0 % rhs)
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Rem<NonZero<Int<RHS_LIMBS>>>
for &Wrapping<Int<LIMBS>>
{
type Output = Wrapping<Int<RHS_LIMBS>>;
fn rem(self, rhs: NonZero<Int<RHS_LIMBS>>) -> Self::Output {
*self % rhs
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Rem<&NonZero<Int<RHS_LIMBS>>>
for &Wrapping<Int<LIMBS>>
{
type Output = Wrapping<Int<RHS_LIMBS>>;
fn rem(self, rhs: &NonZero<Int<RHS_LIMBS>>) -> Self::Output {
*self % *rhs
}
}
impl<const LIMBS: usize, const RHS_LIMBS: usize> Rem<&NonZero<Int<RHS_LIMBS>>>
for Wrapping<Int<LIMBS>>
{
type Output = Wrapping<Int<RHS_LIMBS>>;
fn rem(self, rhs: &NonZero<Int<RHS_LIMBS>>) -> Self::Output {
self % *rhs
}
}
impl<const LIMBS: usize> RemAssign<NonZero<Int<LIMBS>>> for Wrapping<Int<LIMBS>> {
fn rem_assign(&mut self, rhs: NonZero<Int<LIMBS>>) {
*self %= &rhs;
}
}
impl<const LIMBS: usize> RemAssign<&NonZero<Int<LIMBS>>> for Wrapping<Int<LIMBS>> {
fn rem_assign(&mut self, rhs: &NonZero<Int<LIMBS>>) {
*self = Wrapping(self.0 % rhs);
}
}
#[cfg(test)]
mod tests {
use crate::{CtAssign, DivVartime, I128, Int, NonZero, One, Zero};
#[test]
#[allow(clippy::init_numbered_fields)]
fn test_checked_div() {
let min_plus_one = Int {
0: I128::MIN.0.wrapping_add(&I128::ONE.0),
};
let result = I128::MIN.checked_div(&I128::MIN);
assert_eq!(result.unwrap(), I128::ONE);
let result = I128::MIN.checked_div(&I128::MINUS_ONE);
assert!(bool::from(result.is_none()));
let result = I128::MIN.checked_div(&I128::ZERO);
assert!(bool::from(result.is_none()));
let result = I128::MIN.checked_div(&I128::ONE);
assert_eq!(result.unwrap(), I128::MIN);
let result = I128::MIN.checked_div(&I128::MAX);
assert_eq!(result.unwrap(), I128::MINUS_ONE);
let result = I128::MINUS_ONE.checked_div(&I128::MIN);
assert_eq!(result.unwrap(), I128::ZERO);
let result = I128::MINUS_ONE.checked_div(&I128::MINUS_ONE);
assert_eq!(result.unwrap(), I128::ONE);
let result = I128::MINUS_ONE.checked_div(&I128::ZERO);
assert!(bool::from(result.is_none()));
let result = I128::MINUS_ONE.checked_div(&I128::ONE);
assert_eq!(result.unwrap(), I128::MINUS_ONE);
let result = I128::MINUS_ONE.checked_div(&I128::MAX);
assert_eq!(result.unwrap(), I128::ZERO);
let result = I128::ZERO.checked_div(&I128::MIN);
assert_eq!(result.unwrap(), I128::ZERO);
let result = I128::ZERO.checked_div(&I128::MINUS_ONE);
assert_eq!(result.unwrap(), I128::ZERO);
let result = I128::ZERO.checked_div(&I128::ZERO);
assert!(bool::from(result.is_none()));
let result = I128::ZERO.checked_div(&I128::ONE);
assert_eq!(result.unwrap(), I128::ZERO);
let result = I128::ZERO.checked_div(&I128::MAX);
assert_eq!(result.unwrap(), I128::ZERO);
let result = I128::ONE.checked_div(&I128::MIN);
assert_eq!(result.unwrap(), I128::ZERO);
let result = I128::ONE.checked_div(&I128::MINUS_ONE);
assert_eq!(result.unwrap(), I128::MINUS_ONE);
let result = I128::ONE.checked_div(&I128::ZERO);
assert!(bool::from(result.is_none()));
let result = I128::ONE.checked_div(&I128::ONE);
assert_eq!(result.unwrap(), I128::ONE);
let result = I128::ONE.checked_div(&I128::MAX);
assert_eq!(result.unwrap(), I128::ZERO);
let result = I128::MAX.checked_div(&I128::MIN);
assert_eq!(result.unwrap(), I128::ZERO);
let result = I128::MAX.checked_div(&I128::MINUS_ONE);
assert_eq!(result.unwrap(), min_plus_one);
let result = I128::MAX.checked_div(&I128::ZERO);
assert!(bool::from(result.is_none()));
let result = I128::MAX.checked_div(&I128::ONE);
assert_eq!(result.unwrap(), I128::MAX);
let result = I128::MAX.checked_div(&I128::MAX);
assert_eq!(result.unwrap(), I128::ONE);
}
#[test]
fn test_checked_div_floor() {
assert_eq!(
I128::from(8).checked_div_floor(&I128::from(3)).unwrap(),
I128::from(2)
);
assert_eq!(
I128::from(-8).checked_div_floor(&I128::from(3)).unwrap(),
I128::from(-3)
);
assert_eq!(
I128::from(8).checked_div_floor(&I128::from(-3)).unwrap(),
I128::from(-3)
);
assert_eq!(
I128::from(-8).checked_div_floor(&I128::from(-3)).unwrap(),
I128::from(2)
);
}
#[test]
fn test_checked_div_mod_floor() {
let (quotient, remainder) =
I128::MIN.checked_div_rem_floor(&I128::MINUS_ONE.to_nz().unwrap());
assert!(!quotient.is_some().to_bool());
assert_eq!(remainder, I128::ZERO);
}
#[test]
fn div_vartime_through_trait() {
fn myfn<T: DivVartime + Zero + One + CtAssign>(x: T, y: T) -> T {
x.div_vartime(&NonZero::new(y).unwrap())
}
assert_eq!(myfn(I128::from(8), I128::from(3)), I128::from(2));
assert_eq!(myfn(I128::from(-8), I128::from(3)), I128::from(-2));
assert_eq!(myfn(I128::from(8), I128::from(-3)), I128::from(-2));
assert_eq!(myfn(I128::from(-8), I128::from(-3)), I128::from(2));
assert_eq!(myfn(I128::MAX, I128::from(1)), I128::MAX);
assert_eq!(myfn(I128::MAX, I128::MAX), I128::from(1));
}
}