From f500f2b3072a364a27aedcf0e8c71a9783887ce0 Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Tue, 14 Jul 2026 21:24:15 +0100 Subject: [PATCH 1/2] Add from_limbs_checked field element method --- field/src/lib.rs | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/field/src/lib.rs b/field/src/lib.rs index dcefedc..1d42d19 100644 --- a/field/src/lib.rs +++ b/field/src/lib.rs @@ -151,6 +151,22 @@ impl FieldElement

{ } } + pub fn from_limbs_checked(canonical: P::Limbs) -> Option { + let m = P::MODULUS; + let m_slice = m.as_ref(); + let c_slice = canonical.as_ref(); + let mut br = false; + for i in 0..c_slice.len() { + let (_, nb) = c_slice[i].borrowing_sub(m_slice[i], br); + br = nb; + } + if br { + Some(Self::from_limbs_unchecked(canonical)) + } else { + None + } + } + pub fn add(&self, rhs: &Self) -> Self { let a = self.limbs.as_ref(); let b = rhs.limbs.as_ref(); @@ -887,4 +903,29 @@ mod tests { // Precondition for the sqrt formula: p ≡ 3 (mod 4). assert_eq!(Csidh512FieldOrder::MODULUS[0] & 0b11, 0b11); } + + #[test] + fn from_limbs_checked_accepts_canonical() { + assert_eq!( + Fp::from_limbs_checked([0, 0, 0, 0]), + Some(Fp::from_limbs_unchecked([0, 0, 0, 0])), + ); + assert_eq!( + Fp::from_limbs_checked([1, 0, 0, 0]), + Some(Fp::from_limbs_unchecked([1, 0, 0, 0])), + ); + assert_eq!( + Fp::from_limbs_checked(p_minus(1)), + Some(Fp::from_limbs_unchecked(p_minus(1))), + ); + } + + #[test] + fn from_limbs_checked_rejects_modulus_and_above() { + assert!(Fp::from_limbs_checked(P).is_none()); + let mut p_plus_one = P; + p_plus_one[0] = p_plus_one[0].wrapping_add(1); + assert!(Fp::from_limbs_checked(p_plus_one).is_none()); + assert!(Fp::from_limbs_checked([u64::MAX; 4]).is_none()); + } } From bc74332a3a30f22a676ca14778e7961024c0101f Mon Sep 17 00:00:00 2001 From: rustaceanrob Date: Tue, 14 Jul 2026 21:27:44 +0100 Subject: [PATCH 2/2] Add pow_u64 method on field element --- field/src/lib.rs | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/field/src/lib.rs b/field/src/lib.rs index 1d42d19..1b6b678 100644 --- a/field/src/lib.rs +++ b/field/src/lib.rs @@ -258,6 +258,12 @@ impl FieldElement

{ result } + pub fn pow_u64(&self, exp: u64) -> Self { + let mut limbs = P::Limbs::default(); + limbs.as_mut()[0] = exp; + self.pow(limbs) + } + pub fn inv(&self) -> Self { let mut exp = P::MODULUS; { @@ -928,4 +934,30 @@ mod tests { assert!(Fp::from_limbs_checked(p_plus_one).is_none()); assert!(Fp::from_limbs_checked([u64::MAX; 4]).is_none()); } + + #[test] + fn pow_u64_zero_exponent_is_one() { + let a = Fp::from_limbs_unchecked([7, 0, 0, 0]); + assert_eq!(a.pow_u64(0), Fp::one()); + } + + #[test] + fn pow_u64_one_exponent_is_self() { + let a = Fp::from_limbs_unchecked([7, 0, 0, 0]); + assert_eq!(a.pow_u64(1), a); + } + + #[test] + fn pow_u64_small_exponent_matches_repeated_mul() { + let a = Fp::from_limbs_unchecked([3, 0, 0, 0]); + let expected = a * a * a * a * a; + assert_eq!(a.pow_u64(5), expected); + } + + #[test] + fn pow_u64_matches_pow_with_padded_limbs() { + let a = Fp::from_limbs_unchecked([123456789, 0, 0, 0]); + let exp: u64 = 0xDEAD_BEEF_CAFE_BABE; + assert_eq!(a.pow_u64(exp), a.pow([exp, 0, 0, 0])); + } }