Skip to content

Commit

Permalink
Merge branch 'int-twos' into widening-mul
Browse files Browse the repository at this point in the history
  • Loading branch information
erik-3milabs authored Oct 21, 2024
2 parents fc7ae66 + 34290de commit bc9b573
Show file tree
Hide file tree
Showing 4 changed files with 204 additions and 29 deletions.
5 changes: 5 additions & 0 deletions src/const_choice.rs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,11 @@ impl ConstChoice {
Self(self.0 ^ other.0)
}

#[inline]
pub(crate) const fn ne(&self, other: Self) -> Self {
Self::xor(self, other)
}

/// Return `b` if `self` is truthy, otherwise return `a`.
#[inline]
pub(crate) const fn select_word(&self, a: Word, b: Word) -> Word {
Expand Down
92 changes: 91 additions & 1 deletion src/int/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,24 @@ impl<const LIMBS: usize> Int<LIMBS> {
Uint::lt(&lhs.invert_msb().0, &rhs.invert_msb().0)
}

/// Returns the truthy value if `self <= rhs` and the falsy value otherwise.
#[inline]
pub(crate) const fn lte(lhs: &Self, rhs: &Self) -> ConstChoice {
Self::gt(lhs, rhs).not()
}

/// Returns the truthy value if `self > rhs` and the falsy value otherwise.
#[inline]
pub(crate) const fn gt(lhs: &Self, rhs: &Self) -> ConstChoice {
Uint::gt(&lhs.invert_msb().0, &rhs.invert_msb().0)
}

/// Returns the truthy value if `self >= rhs` and the falsy value otherwise.
#[inline]
pub(crate) const fn gte(lhs: &Self, rhs: &Self) -> ConstChoice {
Self::lt(lhs, rhs).not()
}

/// Returns the ordering between `self` and `rhs` as an i8.
/// Values correspond to the Ordering enum:
/// -1 is Less
Expand Down Expand Up @@ -109,7 +121,9 @@ impl<const LIMBS: usize> PartialEq for Int<LIMBS> {

#[cfg(test)]
mod tests {
use crate::{I128, U128};
use subtle::{ConstantTimeGreater, ConstantTimeLess};

use crate::{Int, I128, U128};

#[test]
fn test_is_nonzero() {
Expand Down Expand Up @@ -172,4 +186,80 @@ mod tests {
assert_ne!(true, I128::MINUS_ONE < I128::MINUS_ONE);
assert_ne!(true, I128::MIN < I128::MIN);
}

#[test]
fn ct_gt() {
let a = I128::MIN;
let b = I128::ZERO;
let c = I128::MAX;

assert!(bool::from(b.ct_gt(&a)));
assert!(bool::from(c.ct_gt(&a)));
assert!(bool::from(c.ct_gt(&b)));

assert!(!bool::from(a.ct_gt(&a)));
assert!(!bool::from(b.ct_gt(&b)));
assert!(!bool::from(c.ct_gt(&c)));

assert!(!bool::from(a.ct_gt(&b)));
assert!(!bool::from(a.ct_gt(&c)));
assert!(!bool::from(b.ct_gt(&c)));
}

#[test]
fn gte() {
let a = I128::MIN;
let b = I128::ZERO;
let c = I128::MAX;

assert!(bool::from(Int::gte(&b, &a)));
assert!(bool::from(Int::gte(&c, &a)));
assert!(bool::from(Int::gte(&c, &b)));

assert!(bool::from(Int::gte(&a, &a)));
assert!(bool::from(Int::gte(&b, &b)));
assert!(bool::from(Int::gte(&c, &c)));

assert!(!bool::from(Int::gte(&a, &b)));
assert!(!bool::from(Int::gte(&a, &c)));
assert!(!bool::from(Int::gte(&b, &c)));
}

#[test]
fn ct_lt() {
let a = I128::ZERO;
let b = I128::ONE;
let c = I128::MAX;

assert!(bool::from(a.ct_lt(&b)));
assert!(bool::from(a.ct_lt(&c)));
assert!(bool::from(b.ct_lt(&c)));

assert!(!bool::from(a.ct_lt(&a)));
assert!(!bool::from(b.ct_lt(&b)));
assert!(!bool::from(c.ct_lt(&c)));

assert!(!bool::from(b.ct_lt(&a)));
assert!(!bool::from(c.ct_lt(&a)));
assert!(!bool::from(c.ct_lt(&b)));
}

#[test]
fn lte() {
let a = I128::ZERO;
let b = I128::ONE;
let c = I128::MAX;

assert!(bool::from(Int::lte(&a, &b)));
assert!(bool::from(Int::lte(&a, &c)));
assert!(bool::from(Int::lte(&b, &c)));

assert!(bool::from(Int::lte(&a, &a)));
assert!(bool::from(Int::lte(&b, &b)));
assert!(bool::from(Int::lte(&c, &c)));

assert!(!bool::from(Int::lte(&b, &a)));
assert!(!bool::from(Int::lte(&c, &a)));
assert!(!bool::from(Int::lte(&c, &b)));
}
}
75 changes: 50 additions & 25 deletions src/int/div.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,41 +4,64 @@ use core::ops::Div;

use subtle::{Choice, CtOption};

use crate::{CheckedDiv, ConstChoice, Int, NonZero, Uint};
use crate::{CheckedDiv, ConstChoice, ConstCtOption, Int, NonZero, Uint};

impl<const LIMBS: usize> Int<LIMBS> {
#[inline]
/// Base checked_div_mod operation.
/// Given `(a, b)` computes the quotient and remainder of the absolute
/// Note: this operation rounds towards zero, truncating any fractional part of the exact result.
fn checked_div_mod(
/// Base div_rem operation.
/// Given `(a, b)`, computes the quotient and remainder of their absolute values. Furthermore,
/// returns the signs of `a` and `b`.
fn div_rem_base(
&self,
rhs: &NonZero<Self>,
) -> (Uint<{ LIMBS }>, Uint<{ LIMBS }>, ConstChoice) {
// Step 1: split operands into signs, magnitudes and whether they are zero.
) -> (Uint<{ LIMBS }>, Uint<{ LIMBS }>, ConstChoice, ConstChoice) {
// Step 1: split operands into signs and magnitudes.
let (lhs_mag, lhs_sgn) = self.abs_sign();
let (rhs_mag, rhs_sgn) = rhs.abs_sign();
let (rhs_mag, rhs_sgn) = rhs.0.abs_sign();

// Step 2. Determine if the result should be negated.
// This should be done if and only if lhs and rhs have opposing signs.
// Note: if the lhs is zero, the resulting magnitude will also be zero. Negating zero,
// however, still yields zero, so having a truthy `negate_result` in that scenario is OK.
let opposing_signs = lhs_sgn.xor(rhs_sgn);

// Step 3. Construct result
// Step 2. Divide magnitudes
// safe to unwrap since rhs is NonZero.
let (quotient, remainder) = lhs_mag.div_rem(&NonZero::new(rhs_mag).unwrap());

(quotient, remainder, opposing_signs)
(quotient, remainder, lhs_sgn, rhs_sgn)
}

///
/// Example:
/// ```
/// use crypto_bigint::{I128, NonZero};
/// let (quotient, remainder) = I128::from(8).checked_div_rem(&I128::from(3).to_nz().unwrap());
/// assert_eq!(quotient.unwrap(), I128::from(2));
/// assert_eq!(remainder.unwrap(), I128::from(2));
///
/// let (quotient, remainder) = I128::from(-8).checked_div_rem(&I128::from(3).to_nz().unwrap());
/// assert_eq!(quotient.unwrap(), I128::from(-2));
/// assert_eq!(remainder.unwrap(), I128::from(-2));
///
/// let (quotient, remainder) = I128::from(8).checked_div_rem(&I128::from(-3).to_nz().unwrap());
/// assert_eq!(quotient.unwrap(), I128::from(-2));
/// assert_eq!(remainder.unwrap(), I128::from(2));
///
/// let (quotient, remainder) = I128::from(-8).checked_div_rem(&I128::from(-3).to_nz().unwrap());
/// assert_eq!(quotient.unwrap(), I128::from(2));
/// assert_eq!(remainder.unwrap(), I128::from(-2));
/// ```
pub fn checked_div_rem(
&self,
rhs: &NonZero<Self>,
) -> (ConstCtOption<Self>, ConstCtOption<Self>) {
let (quotient, remainder, lhs_sgn, rhs_sgn) = self.div_rem_base(rhs);
let opposing_signs = lhs_sgn.ne(rhs_sgn);
(
Self::new_from_sign_and_magnitude(opposing_signs, quotient),
Self::new_from_sign_and_magnitude(lhs_sgn, remainder),
)
}

/// Perform checked division, returning a [`CtOption`] which `is_some` only if the rhs != 0.
/// Note: this operation rounds towards zero, truncating any fractional part of the exact result.
pub fn checked_div(&self, rhs: &Self) -> CtOption<Self> {
NonZero::new(*rhs).and_then(|rhs| {
let (quotient, _, opposing_signs) = self.checked_div_mod(&rhs);
Self::new_from_abs_sign(quotient, opposing_signs).into()
})
NonZero::new(*rhs).and_then(|rhs| self.checked_div_rem(&rhs).0.into())
}

/// Perform checked division, returning a [`CtOption`] which `is_some` only if the rhs != 0.
Expand Down Expand Up @@ -66,10 +89,12 @@ impl<const LIMBS: usize> Int<LIMBS> {
/// ```
pub fn checked_div_floor(&self, rhs: &Self) -> CtOption<Self> {
NonZero::new(*rhs).and_then(|rhs| {
let (quotient, remainder, opposing_signs) = self.checked_div_mod(&rhs);
let (quotient, remainder, lhs_sgn, rhs_sgn) = self.div_rem_base(&rhs);

// Increase the quotient by one when lhs and rhs have opposing signs and there
// is a non-zero remainder.
// Increment the quotient when
// - lhs and rhs have opposing signs, and
// - there is a non-zero remainder.
let opposing_signs = lhs_sgn.ne(rhs_sgn);
let increment_quotient = remainder.is_nonzero().and(opposing_signs);
let quotient_sub_one = quotient.wrapping_add(&Uint::ONE);
let quotient = Uint::select(&quotient, &quotient_sub_one, increment_quotient);
Expand Down Expand Up @@ -130,7 +155,7 @@ impl<const LIMBS: usize> Int<LIMBS> {

impl<const LIMBS: usize> CheckedDiv for Int<LIMBS> {
fn checked_div(&self, rhs: &Int<LIMBS>) -> CtOption<Self> {
self.checked_div(rhs)
self.checked_div(rhs).into()
}
}

Expand Down Expand Up @@ -162,7 +187,7 @@ impl<const LIMBS: usize> Div<NonZero<Int<LIMBS>>> for Int<LIMBS> {
type Output = CtOption<Int<LIMBS>>;

fn div(self, rhs: NonZero<Int<LIMBS>>) -> Self::Output {
self.checked_div(&rhs)
self.checked_div(&rhs).into()
}
}

Expand Down
61 changes: 58 additions & 3 deletions src/uint/cmp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
//!
//! By default these are all constant-time and use the `subtle` crate.
use super::Uint;
use crate::{ConstChoice, Limb};
use core::cmp::Ordering;

use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};

use crate::{ConstChoice, Limb};

use super::Uint;

impl<const LIMBS: usize> Uint<LIMBS> {
/// Return `b` if `c` is truthy, otherwise return `a`.
#[inline]
Expand Down Expand Up @@ -64,13 +67,25 @@ impl<const LIMBS: usize> Uint<LIMBS> {
ConstChoice::from_word_mask(borrow.0)
}

/// Returns the truthy value if `self <= rhs` and the falsy value otherwise.
#[inline]
pub(crate) const fn lte(lhs: &Self, rhs: &Self) -> ConstChoice {
Self::gt(lhs, rhs).not()
}

/// Returns the truthy value if `self > rhs` and the falsy value otherwise.
#[inline]
pub(crate) const fn gt(lhs: &Self, rhs: &Self) -> ConstChoice {
let (_res, borrow) = rhs.sbb(lhs, Limb::ZERO);
ConstChoice::from_word_mask(borrow.0)
}

/// Returns the truthy value if `self >= rhs` and the falsy value otherwise.
#[inline]
pub(crate) const fn gte(lhs: &Self, rhs: &Self) -> ConstChoice {
Self::lt(lhs, rhs).not()
}

/// Returns the ordering between `self` and `rhs` as an i8.
/// Values correspond to the Ordering enum:
/// -1 is Less
Expand Down Expand Up @@ -160,10 +175,12 @@ impl<const LIMBS: usize> PartialEq for Uint<LIMBS> {

#[cfg(test)]
mod tests {
use crate::{Integer, Zero, U128};
use core::cmp::Ordering;

use subtle::{ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};

use crate::{Integer, Uint, Zero, U128};

#[test]
fn is_zero() {
assert!(bool::from(U128::ZERO.is_zero()));
Expand Down Expand Up @@ -214,6 +231,25 @@ mod tests {
assert!(!bool::from(b.ct_gt(&c)));
}

#[test]
fn gte() {
let a = U128::ZERO;
let b = U128::ONE;
let c = U128::MAX;

assert!(bool::from(Uint::gte(&b, &a)));
assert!(bool::from(Uint::gte(&c, &a)));
assert!(bool::from(Uint::gte(&c, &b)));

assert!(bool::from(Uint::gte(&a, &a)));
assert!(bool::from(Uint::gte(&b, &b)));
assert!(bool::from(Uint::gte(&c, &c)));

assert!(!bool::from(Uint::gte(&a, &b)));
assert!(!bool::from(Uint::gte(&a, &c)));
assert!(!bool::from(Uint::gte(&b, &c)));
}

#[test]
fn ct_lt() {
let a = U128::ZERO;
Expand All @@ -233,6 +269,25 @@ mod tests {
assert!(!bool::from(c.ct_lt(&b)));
}

#[test]
fn lte() {
let a = U128::ZERO;
let b = U128::ONE;
let c = U128::MAX;

assert!(bool::from(Uint::lte(&a, &b)));
assert!(bool::from(Uint::lte(&a, &c)));
assert!(bool::from(Uint::lte(&b, &c)));

assert!(bool::from(Uint::lte(&a, &a)));
assert!(bool::from(Uint::lte(&b, &b)));
assert!(bool::from(Uint::lte(&c, &c)));

assert!(!bool::from(Uint::lte(&b, &a)));
assert!(!bool::from(Uint::lte(&c, &a)));
assert!(!bool::from(Uint::lte(&c, &b)));
}

#[test]
fn cmp() {
let a = U128::ZERO;
Expand Down

0 comments on commit bc9b573

Please sign in to comment.