use crate::scalar::{ExactFieldScalar, Fp, Fpn, Scalar};
pub(super) fn assert_odd_prime<const P: u128>() {
Fp::<P>::assert_supported_params();
assert!(P != 2, "odd-characteristic form theory needs P odd");
}
pub trait FiniteOddField: ExactFieldScalar + Copy {
fn characteristic_prime() -> u128;
fn field_order() -> u128;
fn is_supported_odd_field() -> bool;
fn from_index(i: u128) -> Self;
fn is_square_value(x: Self) -> bool;
fn ensure_supported() -> Option<()> {
Self::is_supported_odd_field().then_some(())
}
}
impl<const P: u128> FiniteOddField for Fp<P> {
fn characteristic_prime() -> u128 {
P
}
fn field_order() -> u128 {
P
}
fn is_supported_odd_field() -> bool {
Fp::<P>::modulus_is_prime() && P != 2
}
fn from_index(i: u128) -> Self {
Fp::<P>::from_u128(i)
}
fn is_square_value(x: Self) -> bool {
is_square(x)
}
}
impl<const P: u128, const N: usize> FiniteOddField for Fpn<P, N> {
fn characteristic_prime() -> u128 {
P
}
fn field_order() -> u128 {
Fpn::<P, N>::field_order()
}
fn is_supported_odd_field() -> bool {
Fpn::<P, N>::is_supported_field() && P != 2
}
fn from_index(i: u128) -> Self {
let mut digits = [0u128; N];
let mut x = i;
for d in digits.iter_mut() {
*d = x % P;
x /= P;
}
Fpn::<P, N>::from_coeffs(&digits)
}
fn is_square_value(x: Self) -> bool {
x.is_square()
}
}
pub fn is_square<const P: u128>(x: Fp<P>) -> bool {
assert_odd_prime::<P>();
if x.is_zero() {
return true;
}
x.pow((P - 1) / 2) == Fp::<P>::one()
}
pub fn is_square_finite<F: FiniteOddField>(x: F) -> bool {
assert!(
F::is_supported_odd_field(),
"odd-characteristic finite-field form theory needs odd finite fields"
);
F::is_square_value(x)
}
pub fn hilbert_symbol<const P: u128>(a: Fp<P>, b: Fp<P>) -> i128 {
assert_odd_prime::<P>();
for x in 0..P {
for y in 0..P {
for z in 0..P {
if x == 0 && y == 0 && z == 0 {
continue;
}
let (fx, fy, fz) = (
Fp::<P>::from_u128(x),
Fp::<P>::from_u128(y),
Fp::<P>::from_u128(z),
);
let rhs = a.mul(&fx.mul(&fx)).add(&b.mul(&fy.mul(&fy)));
if fz.mul(&fz) == rhs {
return 1;
}
}
}
}
-1
}