use super::Uint;
use crate::{CtChoice, Limb};
use core::cmp::Ordering;
use subtle::{Choice, ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};
impl<const LIMBS: usize> Uint<LIMBS> {
#[inline]
pub(crate) const fn ct_select(a: &Self, b: &Self, c: CtChoice) -> Self {
let mut limbs = [Limb::ZERO; LIMBS];
let mut i = 0;
while i < LIMBS {
limbs[i] = Limb::ct_select(a.limbs[i], b.limbs[i], c);
i += 1;
}
Uint { limbs }
}
#[inline]
pub(crate) const fn ct_swap(a: &Self, b: &Self, c: CtChoice) -> (Self, Self) {
let new_a = Self::ct_select(a, b, c);
let new_b = Self::ct_select(b, a, c);
(new_a, new_b)
}
#[inline]
pub(crate) const fn ct_is_nonzero(&self) -> CtChoice {
let mut b = 0;
let mut i = 0;
while i < LIMBS {
b |= self.limbs[i].0;
i += 1;
}
Limb(b).ct_is_nonzero()
}
pub(crate) const fn ct_is_odd(&self) -> CtChoice {
CtChoice::from_lsb(self.limbs[0].0 & 1)
}
#[inline]
pub(crate) const fn ct_eq(lhs: &Self, rhs: &Self) -> CtChoice {
let mut acc = 0;
let mut i = 0;
while i < LIMBS {
acc |= lhs.limbs[i].0 ^ rhs.limbs[i].0;
i += 1;
}
Limb(acc).ct_is_nonzero().not()
}
#[inline]
pub(crate) const fn ct_lt(lhs: &Self, rhs: &Self) -> CtChoice {
let (_res, borrow) = lhs.sbb(rhs, Limb::ZERO);
CtChoice::from_mask(borrow.0)
}
#[inline]
pub(crate) const fn ct_gt(lhs: &Self, rhs: &Self) -> CtChoice {
let (_res, borrow) = rhs.sbb(lhs, Limb::ZERO);
CtChoice::from_mask(borrow.0)
}
}
impl<const LIMBS: usize> ConstantTimeEq for Uint<LIMBS> {
#[inline]
fn ct_eq(&self, other: &Self) -> Choice {
Uint::ct_eq(self, other).into()
}
}
impl<const LIMBS: usize> ConstantTimeGreater for Uint<LIMBS> {
#[inline]
fn ct_gt(&self, other: &Self) -> Choice {
Uint::ct_gt(self, other).into()
}
}
impl<const LIMBS: usize> ConstantTimeLess for Uint<LIMBS> {
#[inline]
fn ct_lt(&self, other: &Self) -> Choice {
Uint::ct_lt(self, other).into()
}
}
impl<const LIMBS: usize> Eq for Uint<LIMBS> {}
impl<const LIMBS: usize> Ord for Uint<LIMBS> {
fn cmp(&self, other: &Self) -> Ordering {
let is_lt = self.ct_lt(other);
let is_eq = self.ct_eq(other);
if is_lt.into() {
Ordering::Less
} else if is_eq.into() {
Ordering::Equal
} else {
Ordering::Greater
}
}
}
impl<const LIMBS: usize> PartialOrd for Uint<LIMBS> {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl<const LIMBS: usize> PartialEq for Uint<LIMBS> {
fn eq(&self, other: &Self) -> bool {
self.ct_eq(other).into()
}
}
#[cfg(test)]
mod tests {
use crate::{Integer, Zero, U128};
use subtle::{ConstantTimeEq, ConstantTimeGreater, ConstantTimeLess};
#[test]
fn is_zero() {
assert!(bool::from(U128::ZERO.is_zero()));
assert!(!bool::from(U128::ONE.is_zero()));
assert!(!bool::from(U128::MAX.is_zero()));
}
#[test]
fn is_odd() {
assert!(!bool::from(U128::ZERO.is_odd()));
assert!(bool::from(U128::ONE.is_odd()));
assert!(bool::from(U128::MAX.is_odd()));
}
#[test]
fn ct_eq() {
let a = U128::ZERO;
let b = U128::MAX;
assert!(bool::from(a.ct_eq(&a)));
assert!(!bool::from(a.ct_eq(&b)));
assert!(!bool::from(b.ct_eq(&a)));
assert!(bool::from(b.ct_eq(&b)));
}
#[test]
fn ct_gt() {
let a = U128::ZERO;
let b = U128::ONE;
let c = U128::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 ct_lt() {
let a = U128::ZERO;
let b = U128::ONE;
let c = U128::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)));
}
}