use super::limbs::{gte_5_4, mul_4_by_4, reduce_8_mod_4, sub_5_4};
#[macro_export]
macro_rules! impl_field_reduction_constants {
($field:ty) => {
impl $crate::big_num::FieldReductionConstants for $field {
const MODULUS: [u64; 4] =
$crate::big_num::macros::parse_hex_to_limbs(<$field as ff::PrimeField>::MODULUS);
const R_MOD: [u64; 4] = <$field as ff::Field>::ONE.0;
const MONT_INV: u64 = $crate::big_num::macros::compute_mont_inv(Self::MODULUS[0]);
const R512_MOD: [u64; 4] =
$crate::big_num::macros::compute_r512_mod(Self::MODULUS, Self::R_MOD);
const MAX_REDC_SUB_CORRECTIONS: usize =
$crate::big_num::macros::compute_max_redc_sub_corrections(Self::MODULUS);
}
const _: () = assert!(
<$field as $crate::big_num::FieldReductionConstants>::MAX_REDC_SUB_CORRECTIONS <= 8,
"MAX_REDC_SUB_CORRECTIONS too large for efficient reduction"
);
};
}
#[macro_export]
macro_rules! impl_montgomery_limbs {
($field:ty) => {
impl $crate::big_num::montgomery::MontgomeryLimbs for $field {
#[inline]
fn from_limbs(limbs: [u64; 4]) -> Self {
Self(limbs)
}
#[inline]
fn to_limbs(&self) -> &[u64; 4] {
&self.0
}
}
};
}
const fn hex_char_to_nibble(c: u8) -> u8 {
match c {
b'0'..=b'9' => c - b'0',
b'a'..=b'f' => c - b'a' + 10,
b'A'..=b'F' => c - b'A' + 10,
_ => panic!("invalid hex character"),
}
}
pub const fn parse_hex_to_limbs(s: &str) -> [u64; 4] {
let bytes = s.as_bytes();
let (hex_start, hex_len) =
if bytes.len() >= 2 && bytes[0] == b'0' && (bytes[1] == b'x' || bytes[1] == b'X') {
(2, bytes.len() - 2)
} else {
(0, bytes.len())
};
assert!(
hex_len == 64,
"hex string must be 64 characters (excluding optional 0x prefix)"
);
let mut limbs = [0u64; 4];
let mut i = 0;
while i < 4 {
let limb_idx = 3 - i;
let str_offset = hex_start + i * 16;
let mut limb = 0u64;
let mut j = 0;
while j < 16 {
let nibble = hex_char_to_nibble(bytes[str_offset + j]);
limb = (limb << 4) | (nibble as u64);
j += 1;
}
limbs[limb_idx] = limb;
i += 1;
}
limbs
}
pub const fn compute_mont_inv(p0: u64) -> u64 {
let mut inv = 1u64;
inv = inv.wrapping_mul(2u64.wrapping_sub(p0.wrapping_mul(inv)));
inv = inv.wrapping_mul(2u64.wrapping_sub(p0.wrapping_mul(inv)));
inv = inv.wrapping_mul(2u64.wrapping_sub(p0.wrapping_mul(inv)));
inv = inv.wrapping_mul(2u64.wrapping_sub(p0.wrapping_mul(inv)));
inv = inv.wrapping_mul(2u64.wrapping_sub(p0.wrapping_mul(inv)));
inv = inv.wrapping_mul(2u64.wrapping_sub(p0.wrapping_mul(inv)));
inv.wrapping_neg()
}
pub const fn compute_max_redc_sub_corrections(p: [u64; 4]) -> usize {
let mut a = [0u64, 0, 0, 0, 1u64];
let mut count = 0usize;
while gte_5_4(&a, &p) {
a = sub_5_4(&a, &p);
count += 1;
}
count
}
pub const fn compute_r512_mod(p: [u64; 4], r_mod: [u64; 4]) -> [u64; 4] {
let r_squared = mul_4_by_4(&r_mod, &r_mod);
reduce_8_mod_4(&r_squared, &p)
}