use crate::scalar::Fp;
use std::collections::BTreeSet;
fn squares_mod<const P: u128>() -> Vec<u128> {
(0..P).map(|x| (x * x) % P).collect()
}
fn sums_of_n_squares<const P: u128>(n: usize) -> BTreeSet<u128> {
if n == 0 {
return BTreeSet::from([0]);
}
let squares = squares_mod::<P>();
let mut cur: BTreeSet<u128> = squares.iter().copied().collect();
for _ in 1..n {
let mut next = BTreeSet::new();
for &a in &cur {
for &s in &squares {
next.insert((a + s) % P);
}
}
cur = next;
}
cur
}
pub fn is_sum_of_n_squares<const P: u128>(x: Fp<P>, n: usize) -> bool {
sums_of_n_squares::<P>(n).contains(&(x.value() % P))
}
pub fn level<const P: u128>() -> Option<usize> {
if !Fp::<P>::modulus_is_prime() {
return None;
}
let minus_one = (P - 1) % P;
(1..=4).find(|&n| sums_of_n_squares::<P>(n).contains(&minus_one))
}
pub fn pythagoras_number<const P: u128>() -> Option<usize> {
if !Fp::<P>::modulus_is_prime() {
return None;
}
let mut prev = sums_of_n_squares::<P>(1);
for n in 1..=(P as usize + 1) {
let next = sums_of_n_squares::<P>(n + 1);
if next == prev {
return Some(n);
}
prev = next;
}
Some(P as usize)
}
fn is_anisotropic<const P: u128>(qs: &[u128]) -> bool {
let dim = qs.len();
let mut total = 1u128;
for _ in 0..dim {
total *= P;
}
for code in 1..total {
let mut c = code;
let mut s = 0u128;
for &q in qs {
let xi = c % P;
c /= P;
s = (s + q * ((xi * xi) % P)) % P;
}
if s == 0 {
return false; }
}
true
}
fn exists_anisotropic_form<const P: u128>(dim: usize) -> bool {
let mut total = 1u128;
for _ in 0..dim {
total *= P - 1;
}
for code in 0..total {
let mut c = code;
let mut qs = Vec::with_capacity(dim);
for _ in 0..dim {
qs.push(1 + c % (P - 1)); c /= P - 1;
}
if is_anisotropic::<P>(&qs) {
return true;
}
}
false
}
pub fn u_invariant<const P: u128>() -> Option<usize> {
if P == 2 || !Fp::<P>::modulus_is_prime() {
return None;
}
let mut u = 0;
for dim in 1..=4 {
if exists_anisotropic_form::<P>(dim) {
u = dim;
} else {
break; }
}
Some(u)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn level_of_finite_fields() {
assert_eq!(level::<2>(), Some(1)); assert_eq!(level::<3>(), Some(2)); assert_eq!(level::<5>(), Some(1)); assert_eq!(level::<7>(), Some(2)); assert_eq!(level::<13>(), Some(1)); assert_eq!(level::<9>(), None); }
#[test]
fn pythagoras_number_of_finite_fields() {
assert_eq!(pythagoras_number::<2>(), Some(1)); assert_eq!(pythagoras_number::<3>(), Some(2));
assert_eq!(pythagoras_number::<5>(), Some(2));
assert_eq!(pythagoras_number::<7>(), Some(2));
}
#[test]
fn u_invariant_of_finite_fields_is_two() {
assert_eq!(u_invariant::<3>(), Some(2));
assert_eq!(u_invariant::<5>(), Some(2));
assert_eq!(u_invariant::<7>(), Some(2));
assert_eq!(u_invariant::<2>(), None); }
#[test]
fn sum_of_squares_spot_checks() {
assert!(is_sum_of_n_squares::<3>(Fp::<3>::from_u128(2), 2)); assert!(!is_sum_of_n_squares::<3>(Fp::<3>::from_u128(2), 1)); assert!(is_sum_of_n_squares::<5>(Fp::<5>::from_u128(4), 1)); }
}