use super::{nim_inv, nim_mul, nim_pow, nim_square, Nimber};
use crate::scalar::finite_field::FiniteField;
use std::collections::HashMap;
pub fn nim_degree(x: u128) -> u128 {
Nimber(x).degree() as u128
}
pub fn nim_conjugates(x: u128) -> Vec<u128> {
Nimber(x).conjugates().into_iter().map(|n| n.0).collect()
}
pub fn nim_min_poly(x: u128) -> Vec<u128> {
Nimber(x)
.min_poly_monic()
.into_iter()
.map(|c| {
debug_assert!(c.0 <= 1, "minimal-polynomial coefficient left F₂");
c.0
})
.collect()
}
pub fn nim_relative_trace(x: u128, m: u128, e: u128) -> u128 {
Nimber(x).relative_trace_over(m as usize, e as usize).0
}
pub fn nim_relative_norm(x: u128, m: u128, e: u128) -> u128 {
Nimber(x).relative_norm_over(m as usize, e as usize).0
}
pub(super) const ORDER_FACTORS: [u128; 9] =
[3, 5, 17, 257, 641, 65537, 274177, 6700417, 67280421310721];
pub fn nim_multiplicative_order(x: u128) -> Option<u128> {
Nimber(x).multiplicative_order()
}
pub fn nim_is_primitive(x: u128) -> bool {
Nimber(x).is_primitive()
}
pub fn nim_primitive_element() -> u128 {
let mut x = 1u128 << 64;
loop {
if nim_is_primitive(x) {
return x;
}
x += 1;
}
}
#[inline]
fn mulmod(a: u128, b: u128, m: u128) -> u128 {
(a * b) % m
}
fn mod_inv(a: u128, m: u128) -> Option<u128> {
let (mut old_r, mut r) = (a as i128, m as i128);
let (mut old_s, mut s) = (1i128, 0i128);
while r != 0 {
let q = old_r / r;
old_r -= q * r;
std::mem::swap(&mut old_r, &mut r);
old_s -= q * s;
std::mem::swap(&mut old_s, &mut s);
}
if old_r != 1 {
return None;
}
Some(old_s.rem_euclid(m as i128) as u128)
}
fn crt(residues: &[u128], moduli: &[u128]) -> Option<u128> {
let mut e: u128 = 0;
let mut radix: u128 = 1;
for (&r, &m) in residues.iter().zip(moduli) {
let diff = (r % m + m - e % m) % m;
let inv = mod_inv(radix % m, m)?;
let coeff = mulmod(diff, inv, m);
e += coeff * radix; radix = radix.checked_mul(m)?;
}
Some(e)
}
fn bsgs_prime_order(g: u128, h: u128, p: u128) -> Option<u128> {
if h == 1 {
return Some(0);
}
let mut m = (p as f64).sqrt() as u128;
while m * m < p {
m += 1;
}
m = m.max(1);
let mut table: HashMap<u128, u128> = HashMap::with_capacity(m as usize);
let mut cur = 1u128;
for j in 0..m {
table.entry(cur).or_insert(j);
cur = nim_mul(cur, g);
}
let factor = nim_inv(nim_pow(g, m))?; let mut gamma = h;
for i in 0..m {
if let Some(&j) = table.get(&gamma) {
return Some(i * m + j);
}
gamma = nim_mul(gamma, factor);
}
None
}
pub fn nim_discrete_log(base: u128, x: u128) -> Option<u128> {
if base == 0 {
return None;
}
if x == 1 {
return Some(0);
}
if x == base {
return Some(1); }
let n = nim_multiplicative_order(base)?;
let mut moduli = Vec::new();
let mut residues = Vec::new();
for &p in &ORDER_FACTORS {
if n % p != 0 {
continue;
}
let g_p = nim_pow(base, n / p);
let h_p = nim_pow(x, n / p);
residues.push(bsgs_prime_order(g_p, h_p, p)?); moduli.push(p);
}
let e = crt(&residues, &moduli)?;
(nim_pow(base, e) == x).then_some(e)
}
impl FiniteField for Nimber {
fn frobenius(&self) -> Self {
Nimber(nim_square(self.0))
}
fn pow(&self, e: u128) -> Self {
Nimber(nim_pow(self.0, e))
}
fn ext_degree() -> usize {
128
}
fn group_order() -> u128 {
u128::MAX }
fn group_order_factors() -> Vec<u128> {
ORDER_FACTORS.to_vec()
}
fn is_primitive(&self) -> bool {
self.0 != 0
&& ORDER_FACTORS
.iter()
.all(|&p| nim_pow(self.0, u128::MAX / p) != 1)
}
fn discrete_log(&self, x: Nimber) -> Option<u128> {
nim_discrete_log(self.0, x.0)
}
}