Skip to content
Prev Previous commit
Next Next commit
simplify
  • Loading branch information
z-tech committed Mar 30, 2026
commit 583ca8380773af1d89103b1a81393234cf28810e
77 changes: 16 additions & 61 deletions ff-macros/src/small_fp/montgomery_backend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -514,73 +514,28 @@ pub(crate) fn exit_impl(modulus: u128, r_mod_p: u128) -> proc_macro2::TokenStrea
<Self as SmallFpConfig>::mul_assign(a, &one);
}

/// Const-compatible Montgomery modular multiplication using u128 arithmetic.
const fn const_mod_mul(a: u128, b: u128, modulus: u128) -> u128 {
match a.overflowing_mul(b) {
(val, false) => val % modulus,
(_, true) => {
let mut result = 0u128;
let mut base = a % modulus;
let mut exp = b;
while exp > 0 {
if exp & 1 == 1 {
result = if result >= modulus - base {
result - (modulus - base)
} else {
result + base
};
}
base = if base >= modulus - base {
base - (modulus - base)
} else {
base + base
};
exp >>= 1;
}
result
}
}
}

/// Construct a [`SmallFp<Self>`] field element from a sign and a slice of u64 limbs.
///
/// This is the `SmallFp` analogue of [`Fp::from_sign_and_limbs`] and is usable
/// in `const` contexts. The limbs are interpreted as a little-endian
/// integer which is then reduced modulo `MODULUS` and converted to
/// Montgomery form.
pub const fn from_sign_and_limbs(is_positive: bool, limbs: &[u64]) -> SmallFp<Self> {
// This is the `SmallFp` analogue of [`MontFp!`] to support const initialization
pub const fn from_u128(value: u128) -> SmallFp<Self> {
const MODULUS: u128 = #modulus;
const R_MOD_P: u128 = #r_mod_p;

// Build u128 from limbs (little-endian)
let mut val: u128 = 0;
let mut i = 0;
while i < limbs.len() && i < 2 {
val |= (limbs[i] as u128) << (64 * i);
i += 1;
// const-compatible modular multiplication via double-and-add
// Safe from overflow: modulus < 2^127 so a,result < 2^127 and all additions fit u128
const fn mod_mul(mut a: u128, mut b: u128, m: u128) -> u128 {
a %= m;
let mut result = 0u128;
while b > 0 {
if b & 1 == 1 {
result = (result + a) % m;
}
a = (a + a) % m;
b >>= 1;
}
result
}

val %= MODULUS;
let mont = Self::const_mod_mul(val, R_MOD_P, MODULUS);

let mont = if is_positive {
mont
} else if mont == 0 {
0
} else {
MODULUS - mont
};

SmallFp::from_raw(mont as <Self as SmallFpConfig>::T)
}

/// Construct a [`SmallFp<Self>`] from a `u128` value in a const context.
pub const fn from_u128(value: u128) -> SmallFp<Self> {
const MODULUS: u128 = #modulus;
const R_MOD_P: u128 = #r_mod_p;

let val = value % MODULUS;
let mont = Self::const_mod_mul(val, R_MOD_P, MODULUS);
let mont = mod_mul(val, R_MOD_P, MODULUS);
SmallFp::from_raw(mont as <Self as SmallFpConfig>::T)
}
}
Expand Down
35 changes: 1 addition & 34 deletions test-curves/src/smallfp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ mod tests {

mod const_constructors {
use super::*;
use ark_ff::{One, PrimeField, Zero};
use ark_ff::{One, Zero};

#[test]
fn test_from_u128_zero() {
Expand Down Expand Up @@ -113,46 +113,13 @@ mod tests {
);
}

#[test]
fn test_from_sign_and_limbs_positive() {
let a: SmallFp64Goldilock = SmallFp64GoldilockConfig::from_sign_and_limbs(true, &[7]);
let expected: SmallFp64Goldilock = SmallFp64GoldilockConfig::from_u128(7);
assert_eq!(a, expected);
}

#[test]
fn test_from_sign_and_limbs_negative() {
let a: SmallFp64Goldilock = SmallFp64GoldilockConfig::from_sign_and_limbs(false, &[1]);
let one: SmallFp64Goldilock = SmallFp64GoldilockConfig::from_u128(1);
assert_eq!(a, -one, "from_sign_and_limbs(false, [1]) should be -1");
}

#[test]
fn test_from_sign_and_limbs_negative_zero() {
let a: SmallFp64Goldilock = SmallFp64GoldilockConfig::from_sign_and_limbs(false, &[0]);
assert!(a.is_zero(), "-0 should be 0");
}

#[test]
fn test_from_sign_and_limbs_two_limbs() {
let val: u128 = (1u128 << 64) + 42;
let lo = val as u64;
let hi = (val >> 64) as u64;
let const_elem: SmallFp128 = SmallFp128Config::from_sign_and_limbs(true, &[lo, hi]);
let runtime_elem = SmallFp128::from(val);
assert_eq!(const_elem, runtime_elem, "two-limb from_sign_and_limbs mismatch");
}

#[test]
fn test_const_context() {
const SEVEN: SmallFp64Goldilock = SmallFp64GoldilockConfig::from_u128(7);
const FORTY_TWO: SmallFp128 = SmallFp128Config::from_u128(42);
const NEG_ONE: SmallFp64Goldilock =
SmallFp64GoldilockConfig::from_sign_and_limbs(false, &[1]);

assert_eq!(SEVEN, SmallFp64Goldilock::from(7u128));
assert_eq!(FORTY_TWO, SmallFp128::from(42u128));
assert_eq!(NEG_ONE, -SmallFp64Goldilock::from(1u128));
}
}
}
Loading