Skip to content

Commit

Permalink
Make Uint::gcd calculation infallible (#619)
Browse files Browse the repository at this point in the history
See also: #618

The computation was previously fallible because even values were
unsupported.

Now that they're supported, the computation no longer needs to be
fallible.

We can eventually remove `type Output` from the `Gcd` trait after a
similar treatment is applied to `BoxedUint` (see #618).
  • Loading branch information
tarcieri authored Jul 13, 2024
1 parent dbc02cb commit 001f89f
Show file tree
Hide file tree
Showing 2 changed files with 19 additions and 24 deletions.
41 changes: 18 additions & 23 deletions src/uint/gcd.rs
Original file line number Diff line number Diff line change
@@ -1,16 +1,13 @@
//! Support for computing the greatest common divisor of two `Uint`s.
use crate::{
modular::BernsteinYangInverter, ConstChoice, ConstCtOption, Gcd, Odd, PrecomputeInverter, Uint,
};
use subtle::CtOption;
use crate::{modular::BernsteinYangInverter, ConstChoice, Gcd, Odd, PrecomputeInverter, Uint};

impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> Uint<SAT_LIMBS>
where
Odd<Self>: PrecomputeInverter<Inverter = BernsteinYangInverter<SAT_LIMBS, UNSAT_LIMBS>>,
{
/// Compute the greatest common divisor (GCD) of this number and another.
pub const fn gcd(&self, rhs: &Self) -> ConstCtOption<Self> {
pub const fn gcd(&self, rhs: &Self) -> Self {
let k1 = self.trailing_zeros();
let k2 = rhs.trailing_zeros();

Expand All @@ -24,22 +21,20 @@ where
let f = Self::select(&s1, &s2, s2.is_odd().not());
let g = Self::select(&s1, &s2, s2.is_odd());

let ret = <Odd<Self> as PrecomputeInverter>::Inverter::gcd(&f, &g);
ConstCtOption::new(
ret.overflowing_shl(k).unwrap_or(Self::ZERO),
f.is_nonzero().and(g.is_odd()),
)
<Odd<Self> as PrecomputeInverter>::Inverter::gcd(&f, &g)
.overflowing_shl(k)
.unwrap_or(Self::ZERO)
}
}

impl<const SAT_LIMBS: usize, const UNSAT_LIMBS: usize> Gcd for Uint<SAT_LIMBS>
where
Odd<Self>: PrecomputeInverter<Inverter = BernsteinYangInverter<SAT_LIMBS, UNSAT_LIMBS>>,
{
type Output = CtOption<Uint<SAT_LIMBS>>;
type Output = Uint<SAT_LIMBS>;

fn gcd(&self, rhs: &Self) -> CtOption<Self> {
self.gcd(rhs).into()
fn gcd(&self, rhs: &Self) -> Self {
self.gcd(rhs)
}
}

Expand All @@ -63,39 +58,39 @@ mod tests {
// Two semiprimes with no common factors
let f = U256::from(59u32 * 67);
let g = U256::from(61u32 * 71);
let gcd = f.gcd(&g).unwrap();
let gcd = f.gcd(&g);
assert_eq!(gcd, U256::ONE);
}

#[test]
fn gcd_nonprime() {
let f = U256::from(4391633u32);
let g = U256::from(2022161u32);
let gcd = f.gcd(&g).unwrap();
let gcd = f.gcd(&g);
assert_eq!(gcd, U256::from(1763u32));
}

#[test]
fn gcd_zero() {
assert!(U256::ZERO.gcd(&U256::ZERO).is_none().is_true_vartime());
assert!(U256::ZERO.gcd(&U256::ONE).is_none().is_true_vartime());
assert!(U256::ONE.gcd(&U256::ZERO).is_none().is_true_vartime());
assert_eq!(U256::ZERO.gcd(&U256::ZERO), U256::ZERO);
assert_eq!(U256::ZERO.gcd(&U256::ONE), U256::ONE);
assert_eq!(U256::ONE.gcd(&U256::ZERO), U256::ONE);
}

#[test]
fn gcd_one() {
let f = U256::ONE;
assert_eq!(U256::ONE, f.gcd(&U256::ONE).unwrap());
assert_eq!(U256::ONE, f.gcd(&U256::from(2u8)).unwrap());
assert_eq!(U256::ONE, f.gcd(&U256::ONE));
assert_eq!(U256::ONE, f.gcd(&U256::from(2u8)));
}

#[test]
fn gcd_two() {
let f = U256::from_u8(2);
assert_eq!(f, f.gcd(&f).unwrap());
assert_eq!(f, f.gcd(&f));

let g = U256::from_u8(4);
assert_eq!(f, f.gcd(&g).unwrap());
assert_eq!(f, g.gcd(&f).unwrap());
assert_eq!(f, f.gcd(&g));
assert_eq!(f, g.gcd(&f));
}
}
2 changes: 1 addition & 1 deletion tests/uint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,7 @@ proptest! {
let g_bi = to_biguint(&g);

let expected = to_uint(f_bi.gcd(&g_bi));
let actual = f.gcd(&g).unwrap();
let actual = f.gcd(&g);
assert_eq!(expected, actual);
}

Expand Down

0 comments on commit 001f89f

Please sign in to comment.