use alloc::vec;
use alloc::vec::Vec;
use crate::int::Int;
use crate::poly::Poly;
use crate::random::SeedRng;
use crate::ring::{FiniteField, Ring};
fn is_constant<T: Ring>(f: &Poly<T>) -> bool {
f.degree().is_none_or(|d| d == 0)
}
fn field_pow<T: Ring>(base: &T, e: &Int) -> T {
let mut result = base.one();
let mut b = base.clone();
for i in 0..e.bit_len() {
if e.bit(i) {
result = result * b.clone();
}
let sq = b.clone() * b.clone();
b = sq;
}
result
}
fn poly_powmod<T: FiniteField>(base: &Poly<T>, e: &Int, modulus: &Poly<T>) -> Poly<T> {
let one = modulus
.leading()
.expect("poly_powmod: modulus must be nonzero")
.one();
let mut result = Poly::constant(one);
let mut b = base.rem(modulus);
for i in 0..e.bit_len() {
if e.bit(i) {
result = result.mul(&b).rem(modulus);
}
b = b.mul(&b).rem(modulus);
}
result
}
fn log_p(q: &Int, p: &Int) -> usize {
let mut s = 0usize;
let mut t = q.clone();
while t > Int::ONE {
t = t.div_exact(p);
s += 1;
}
s
}
fn pth_root<T: FiniteField>(c: &Poly<T>, p: usize, p_int: &Int) -> Poly<T> {
let q_over_p = c
.leading()
.expect("pth_root: nonzero")
.order()
.div_exact(p_int);
let coeffs: Vec<T> = c
.coeffs()
.iter()
.step_by(p)
.map(|coef| field_pow(coef, &q_over_p))
.collect();
Poly::new(coeffs)
}
fn sff_into<T: FiniteField>(
f: &Poly<T>,
scale: usize,
p_int: &Int,
out: &mut Vec<(Poly<T>, usize)>,
) {
let mut c = f.gcd(&f.derivative());
let mut w = f.div_rem(&c).0;
let mut i = 1usize;
while !is_constant(&w) {
let y = w.gcd(&c);
let fac = w.div_rem(&y).0; if !is_constant(&fac) {
out.push((fac.monic(), i * scale));
}
c = c.div_rem(&y).0;
w = y;
i += 1;
}
if !is_constant(&c) {
let p = p_int
.to_u64()
.expect("characteristic fits in u64 for a p-th-power factor") as usize;
let root = pth_root(&c, p, p_int);
sff_into(&root, scale * p, p_int, out);
}
}
fn squarefree<T: FiniteField>(f: &Poly<T>) -> Vec<(Poly<T>, usize)> {
let p = f.leading().expect("squarefree: nonzero").characteristic();
let mut out = Vec::new();
sff_into(f, 1, &p, &mut out);
out
}
fn distinct_degree<T: FiniteField>(f: &Poly<T>) -> Vec<(usize, Poly<T>)> {
let sample = f.leading().expect("distinct_degree: nonzero").clone();
let q = sample.order();
let x = Poly::monomial(sample.one(), 1);
let mut out = Vec::new();
let mut fstar = f.clone();
let mut d = 1usize;
let mut h = poly_powmod(&x, &q, &fstar); while fstar.degree().is_some_and(|deg| deg >= 2 * d) {
let g = fstar.gcd(&h.sub(&x)); if g.degree().is_some_and(|dg| dg > 0) {
out.push((d, g.clone()));
fstar = fstar.div_rem(&g).0;
}
d += 1;
if fstar.degree().is_some_and(|deg| deg >= 2 * d) {
h = poly_powmod(&h, &q, &fstar); }
}
if let Some(deg) = fstar.degree().filter(|°| deg > 0) {
out.push((deg, fstar));
}
out
}
fn seed_rng_from<T: FiniteField>(f: &Poly<T>) -> SeedRng {
let q = f.leading().and_then(|c| c.order().to_u64()).unwrap_or(0);
let deg = f.degree().unwrap_or(0) as u64;
let seed = 0x9E37_79B9_7F4A_7C15u64
.wrapping_mul(deg.wrapping_add(1))
.wrapping_add(q)
.wrapping_add(f.coeffs().len() as u64);
SeedRng::new(seed | 1)
}
fn random_poly<T: FiniteField>(n: usize, sample: &T, rng: &mut SeedRng) -> Poly<T> {
let q = sample.order();
let coeffs: Vec<T> = (0..n)
.map(|_| {
let idx = Int::random_below(&q, rng).unwrap_or(Int::ZERO);
sample.from_index(&idx)
})
.collect();
Poly::new(coeffs)
}
fn equal_degree<T: FiniteField>(f: &Poly<T>, d: usize) -> Vec<Poly<T>> {
let total = f.degree().expect("equal_degree: nonzero");
let r = total / d;
if r <= 1 {
return vec![f.monic()];
}
let sample = f.leading().unwrap().clone();
let q = sample.order();
let p = sample.characteristic();
let is_char2 = p == Int::from_u64(2);
let (exp_odd, trace_len) = if is_char2 {
(Int::ZERO, log_p(&q, &p) * d)
} else {
let half = q.pow(d as u32).sub(&Int::ONE).div_exact(&Int::from_u64(2));
(half, 0)
};
let mut rng = seed_rng_from(f);
let mut factors = vec![f.monic()];
while factors.len() < r {
let a = random_poly(total, &sample, &mut rng);
let b = if is_char2 {
let mut term = a.rem(f);
let mut acc = term.clone();
for _ in 1..trace_len {
term = poly_powmod(&term, &p, f);
acc = acc.add(&term);
}
acc
} else {
poly_powmod(&a, &exp_odd, f).sub(&Poly::constant(sample.one()))
};
let mut next = Vec::with_capacity(factors.len());
for h in core::mem::take(&mut factors) {
let dh = h.degree().unwrap();
if dh == d {
next.push(h);
continue;
}
let g = h.gcd(&b);
let dg = g.degree().unwrap_or(0);
if dg > 0 && dg < dh {
let other = h.div_rem(&g).0;
next.push(g.monic());
next.push(other.monic());
} else {
next.push(h);
}
}
factors = next;
}
factors
}
fn factor_ff<T: FiniteField>(f: &Poly<T>) -> Vec<(Poly<T>, usize)> {
if is_constant(f) {
return Vec::new();
}
let mut out = Vec::new();
for (sqfree, mult) in squarefree(&f.monic()) {
for (d, g_d) in distinct_degree(&sqfree) {
for irr in equal_degree(&g_d, d) {
out.push((irr, mult));
}
}
}
out
}
pub trait FactorOverField<T: FiniteField> {
fn factor(&self) -> Vec<(Poly<T>, usize)>;
fn is_irreducible(&self) -> bool;
fn squarefree_factorization(&self) -> Vec<(Poly<T>, usize)>;
}
impl<T: FiniteField> FactorOverField<T> for Poly<T> {
fn factor(&self) -> Vec<(Poly<T>, usize)> {
factor_ff(self)
}
fn is_irreducible(&self) -> bool {
if is_constant(self) {
return false;
}
let factors = factor_ff(self);
factors.len() == 1 && factors[0].1 == 1
}
fn squarefree_factorization(&self) -> Vec<(Poly<T>, usize)> {
if is_constant(self) {
return Vec::new();
}
squarefree(&self.monic())
}
}