Skip to main content

cas_poly/
factor.rs

1//! 一元因式分解(M4,设计 §5):ℚ[x] → 整数本原部分 → Yun 平方自由分解
2//! → 模 p Cantor–Zassenhaus(DDF+EDF)→ 线性 Hensel 提升(Mignotte 界,
3//! 二叉树两因子逐层)→ 确定性子集组合。
4//!
5//! **确定性**:CZ 的随机多项式取自固定种子的 xorshift,质数表固定,
6//! 子集组合按二进制序枚举——同输入恒得同输出(D6 纪律)。
7//!
8//! 内部表示:稠密系数向量(index = 次数)。`IPoly` 为 i128 整系数;
9//! `Vec<u64>` 配模数 p 或 p^k(系数 ∈ [0, 模数),u128 中转)。
10
11use crate::Poly;
12use cas_domain::{Integer, Rational};
13use std::sync::Arc;
14
15const PRIMES: &[u64] = &[
16    3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73,
17];
18
19/// CZ 随机源(固定种子)。
20pub(crate) struct Det(u64);
21impl Det {
22    pub(crate) fn new() -> Self {
23        Det(0xC0FF_EE01)
24    }
25    pub(crate) fn next(&mut self) -> u64 {
26        let mut x = self.0 | 1;
27        x ^= x << 13;
28        x ^= x >> 7;
29        x ^= x << 17;
30        self.0 = x;
31        x
32    }
33}
34
35// ── 稠密整系数多项式 ──────────────────────────────────────────
36
37type IPoly = Vec<i128>;
38
39fn ip_trim(v: &mut IPoly) {
40    while v.last() == Some(&0) {
41        v.pop();
42    }
43}
44
45fn ip_deg(v: &IPoly) -> i32 {
46    v.len() as i32 - 1
47}
48
49/// 精确除法(首项系数须逐项整除,余数须为零),否则 None。
50fn ip_exact_div(a: &IPoly, b: &IPoly) -> Option<IPoly> {
51    if b.is_empty() {
52        return None;
53    }
54    if a.is_empty() {
55        return Some(vec![]);
56    }
57    if ip_deg(a) < ip_deg(b) {
58        return None;
59    }
60    let db = ip_deg(b) as usize;
61    let lb = *b.last()?;
62    let mut r = a.clone();
63    let mut q = vec![0i128; a.len() - b.len() + 1];
64    while ip_deg(&r) >= ip_deg(b) && !r.is_empty() {
65        let dr = ip_deg(&r) as usize;
66        let lr = *r.last()?;
67        if lr % lb != 0 {
68            return None;
69        }
70        let t = lr / lb;
71        q[dr - db] = t;
72        for (i, &c) in b.iter().enumerate() {
73            // 错误候选的试除中间量可指数增长:i128 乘法触界视为不可整除,
74            // 跳过该候选(真因子除法的中间量受 Mignotte 型界控制,远不及 2^127)
75            r[dr - db + i] = r[dr - db + i].checked_sub(t.checked_mul(c)?)?;
76        }
77        ip_trim(&mut r);
78    }
79    if r.is_empty() { Some(q) } else { None }
80}
81
82/// 本原部分与 ℤ 内容:f = cont · pp,pp 整系数且系数 gcd 为 1、首项为正。
83fn ip_primitive(f: &IPoly) -> (i128, IPoly) {
84    if f.is_empty() {
85        return (0, vec![]);
86    }
87    let mut g: i128 = 0;
88    for &c in f {
89        g = gcd_i128(g, c.abs());
90    }
91    if g == 0 {
92        g = 1;
93    }
94    let mut pp: IPoly = f.iter().map(|&c| c / g).collect();
95    if *pp.last().unwrap() < 0 {
96        for c in &mut pp {
97            *c = -*c;
98        }
99        g = -g;
100    }
101    (g, pp)
102}
103
104fn gcd_i128(mut a: i128, mut b: i128) -> i128 {
105    while b != 0 {
106        let r = a % b;
107        a = b;
108        b = r;
109    }
110    a
111}
112
113// ── 𝔽_p 稠密多项式 ────────────────────────────────────────────
114
115type Fp = Vec<u64>;
116
117fn fp_trim(v: &mut Fp) {
118    while v.last() == Some(&0) {
119        v.pop();
120    }
121}
122
123fn fp_deg(v: &Fp) -> i32 {
124    v.len() as i32 - 1
125}
126
127fn fp_monic(mut v: Fp, p: u64) -> Fp {
128    fp_trim(&mut v);
129    if let Some(&l) = v.last() {
130        let inv = fp_pow(l, p - 2, p);
131        for c in &mut v {
132            *c = *c % p * inv % p;
133        }
134    }
135    v
136}
137
138fn fp_pow(mut b: u64, mut e: u64, p: u64) -> u64 {
139    let mut r = 1u64;
140    b %= p;
141    while e > 0 {
142        if e & 1 == 1 {
143            r = r * b % p;
144        }
145        b = b * b % p;
146        e >>= 1;
147    }
148    r
149}
150
151fn fp_sub(a: &Fp, b: &Fp, p: u64) -> Fp {
152    let mut out = vec![0u64; a.len().max(b.len())];
153    for (i, &x) in a.iter().enumerate() {
154        out[i] = x;
155    }
156    for (i, &y) in b.iter().enumerate() {
157        out[i] = (out[i] + p - y % p) % p;
158    }
159    fp_trim(&mut out);
160    out
161}
162
163fn fp_mul(a: &Fp, b: &Fp, p: u64) -> Fp {
164    if a.is_empty() || b.is_empty() {
165        return vec![];
166    }
167    let mut out = vec![0u64; a.len() + b.len() - 1];
168    for (i, &x) in a.iter().enumerate() {
169        if x == 0 {
170            continue;
171        }
172        for (j, &y) in b.iter().enumerate() {
173            out[i + j] =
174                ((out[i + j] as u128 + x as u128 * y as u128 % p as u128) % p as u128) as u64;
175        }
176    }
177    fp_trim(&mut out);
178    out
179}
180
181fn fp_divrem(a: &Fp, b: &Fp, p: u64) -> (Fp, Fp) {
182    assert!(!b.is_empty(), "fp 除式为零");
183    let mut r = a.clone();
184    let mut q = vec![0u64; (a.len() as i32 - fp_deg(b)).max(0) as usize + 1];
185    let db = fp_deg(b) as usize;
186    let inv = fp_pow(*b.last().unwrap(), p - 2, p);
187    while !r.is_empty() && fp_deg(&r) >= db as i32 {
188        let dr = r.len() - 1;
189        let t = *r.last().unwrap() * inv % p;
190        q[dr - db] = t;
191        for (i, &c) in b.iter().enumerate() {
192            r[dr - db + i] = (r[dr - db + i] + p - t * c % p) % p;
193        }
194        fp_trim(&mut r);
195    }
196    fp_trim(&mut q);
197    (q, r)
198}
199
200/// 扩展 Euclid:返回 (monic g, s, t),s·a + t·b = g。
201fn fp_xgcd(a: &Fp, b: &Fp, p: u64) -> (Fp, Fp, Fp) {
202    let (mut r0, mut r1) = (a.to_vec(), b.to_vec());
203    let (mut s0, mut s1) = (vec![1u64], vec![]);
204    let (mut t0, mut t1) = (vec![], vec![1u64]);
205    while !r1.is_empty() {
206        let (q, r) = fp_divrem(&r0, &r1, p);
207        r0 = r1;
208        r1 = r;
209        let s2 = fp_sub(&s0, &fp_mul(&q, &s1, p), p);
210        s0 = s1;
211        s1 = s2;
212        let t2 = fp_sub(&t0, &fp_mul(&q, &t1, p), p);
213        t0 = t1;
214        t1 = t2;
215    }
216    if r0.is_empty() {
217        return (vec![], vec![], vec![]);
218    }
219    let inv = fp_pow(*r0.last().unwrap(), p - 2, p);
220    let mul = |v: &Fp| -> Fp { v.iter().map(|&c| c * inv % p).collect() };
221    (mul(&r0), mul(&s0), mul(&t0))
222}
223
224fn fp_gcd(a: &Fp, b: &Fp, p: u64) -> Fp {
225    fp_xgcd(a, b, p).0
226}
227
228fn fp_deriv(a: &Fp, p: u64) -> Fp {
229    let mut out = vec![0u64; a.len().saturating_sub(1)];
230    for i in 1..a.len() {
231        out[i - 1] = a[i] * (i as u64) % p;
232    }
233    fp_trim(&mut out);
234    out
235}
236
237/// x^e mod f(平方乘,指数 u128——p^d 类指数会超 u64)。
238fn fp_powmod(mut b: Fp, mut e: u128, f: &Fp, p: u64) -> Fp {
239    let mut r: Fp = vec![1];
240    fn rem(v: Fp, f: &Fp, p: u64) -> Fp {
241        let (_, r) = fp_divrem(&v, f, p);
242        r
243    }
244    b = rem(b, f, p);
245    while e > 0 {
246        if e & 1 == 1 {
247            r = rem(fp_mul(&r, &b, p), f, p);
248        }
249        b = rem(fp_mul(&b, &b, p), f, p);
250        e >>= 1;
251    }
252    r
253}
254
255/// 确定性随机 𝔽_p 多项式,次数 < n 且非零。
256fn fp_rand(n: usize, p: u64, det: &mut Det) -> Fp {
257    let mut v: Fp = (0..n).map(|_| det.next() % p).collect();
258    fp_trim(&mut v);
259    v
260}
261
262/// 不同次数分解(f monic squarefree):返回 (次数 d 的因子之积, d) 列表。
263fn ddf(f: &Fp, p: u64) -> Vec<(Fp, u32)> {
264    let mut out = vec![];
265    let mut fcur = f.clone();
266    let mut h: Fp = vec![0, 1]; // x
267    let mut d = 1u32;
268    loop {
269        h = fp_powmod(h, p as u128, &fcur, p); // x^{p^d}
270        // DDF 判据:gcd(f, x^{p^d} − x)——减 x,不是减 1
271        let xx: Fp = vec![0, 1];
272        let g = fp_gcd(&fcur, &fp_sub(&h, &xx, p), p);
273        if fp_deg(&g) > 0 {
274            let q = fp_monic(g, p);
275            // 除掉该因子后应取商(余式必为零)
276            let (quot, rem) = fp_divrem(&fcur, &q, p);
277            debug_assert!(rem.is_empty(), "ddf 因子必整除");
278            out.push((q.clone(), d));
279            fcur = quot;
280            // 关键:h 须对"剩余部分"取模(新 fcur),对提取因子取模会断
281            // 同余链 x^{p^d} ≡ h (mod fcur),使后续轮次的 d 归属整体慢一拍
282            //(x^34-1 实测:16 次因子被标到 d=17)
283            h = {
284                let (_, r) = fp_divrem(&h, &fcur, p);
285                r
286            };
287        }
288        let df = fp_deg(&fcur);
289        if df <= 0 {
290            break;
291        }
292        if df <= d as i32 {
293            if df > 0 {
294                out.push((fcur.clone(), df as u32));
295            }
296            break;
297        }
298        d += 1;
299    }
300    out
301}
302
303/// 等次数分解(Cantor–Zassenhaus,f 的全部不可约因子次数 = d)。
304fn edf(f: &Fp, d: u32, p: u64, det: &mut Det) -> Vec<Fp> {
305    let n = fp_deg(f);
306    if n <= d as i32 {
307        return vec![f.clone()];
308    }
309    loop {
310        let q = fp_rand(f.len(), p, det);
311        if q.is_empty() {
312            continue;
313        }
314        // t = q^{(p^d − 1)/2} mod f
315        let e = ((p as u128).pow(d) - 1) / 2;
316        let t = fp_powmod(q, e, f, p);
317        if t.is_empty() {
318            continue;
319        }
320        let one: Fp = vec![1];
321        let g = fp_gcd(f, &fp_sub(&t, &one, p), p);
322        let dg = fp_deg(&g);
323        if dg > 0 && dg < n {
324            // 剩余因子 = 商(g 整除 f,余式恒为零)
325            let (quot, rem) = fp_divrem(f, &g, p);
326            debug_assert!(rem.is_empty(), "edf 因子必整除");
327            let mut out = edf(&fp_monic(g, p), d, p, det);
328            out.extend(edf(&fp_monic(quot, p), d, p, det));
329            return out;
330        }
331    }
332}
333
334/// 模 p 完全分解(monic squarefree 输入)→ monic 不可约因子列表。
335fn cz_factor(f_monic_sqfree: &Fp, p: u64, det: &mut Det) -> Vec<Fp> {
336    let mut out = vec![];
337    for (g, d) in ddf(f_monic_sqfree, p) {
338        out.extend(edf(&g, d, p, det));
339    }
340    out
341}
342
343// ── 线性两因子 Hensel(模数 p^step,s·a + t·b ≡ 1 mod p 恒有效)──
344
345/// 把 A ≡ a·b (mod p) 提升到 A ≡ a'·b' (mod p^k)。A、a、b 为非负稠密
346/// 系数(mod p^k 视域)。返回 (a', b')。
347#[allow(clippy::too_many_arguments)]
348/// 提升不变量破坏时返回 None(保守回退:整体不分解,绝不产生错误因子)。
349/// 已知在高次复合因子上偶发(见 DESIGN §17 挂账),修复前以保守行为兜底。
350fn hensel2(
351    target: &[u64],
352    a0: &Fp,
353    b0: &Fp,
354    _s: &Fp,
355    t: &Fp,
356    p: u64,
357    k: u32,
358    pk: u64,
359) -> Option<(Vec<u64>, Vec<u64>)> {
360    let mut a: Vec<u64> = a0.to_vec();
361    let mut b: Vec<u64> = b0.to_vec();
362    let mut step_mod = p; // p^step
363    for _step in 1..k {
364        // prod = a·b mod p^{step+1}
365        let next_mod = step_mod * p; // p^{step+1}
366        let prod = umul(&a, &b, next_mod);
367        // err = (A − prod)/p^step mod p
368        let mut err: Fp = vec![];
369        for i in 0..prod.len().max(target.len()) {
370            let ai = target.get(i).copied().unwrap_or(0) % next_mod;
371            let pi = prod.get(i).copied().unwrap_or(0);
372            let diff = (ai + next_mod - pi) % next_mod;
373            if diff % step_mod != 0 {
374                return None; // 提升不变量破坏:保守放弃
375            }
376            err.push(diff / step_mod % p);
377        }
378        fp_trim(&mut err);
379        if !err.is_empty() {
380            // 解 σ·b + τ·a ≡ err (mod p):σ = (err·t) mod a 后,τ 由精确商
381            // (err − σ·b)/a 给出——两者不能各自独立取模(否则差 ab 的倍数)
382            let sig = {
383                let prod_e = fp_mul(&err, t, p);
384                let (_, r) = fp_divrem(&prod_e, a0, p);
385                r
386            };
387            let tau = {
388                let pr = fp_sub(&err, &fp_mul(&sig, b0, p), p);
389                let (q, r) = fp_divrem(&pr, a0, p);
390                if !r.is_empty() {
391                    return None; // 校正整除性破坏:保守放弃
392                }
393                q
394            };
395            for (i, &c) in sig.iter().enumerate() {
396                a[i] = (a[i] + step_mod % pk * c % pk) % pk;
397            }
398            for (i, &c) in tau.iter().enumerate() {
399                b[i] = (b[i] + step_mod % pk * c % pk) % pk;
400            }
401        }
402        step_mod = next_mod;
403    }
404    trim_u(&mut a);
405    trim_u(&mut b);
406    Some((a, b))
407}
408
409fn trim_u(v: &mut Vec<u64>) {
410    while v.last() == Some(&0) {
411        v.pop();
412    }
413}
414
415fn umul(a: &[u64], b: &[u64], m: u64) -> Vec<u64> {
416    if a.is_empty() || b.is_empty() {
417        return vec![];
418    }
419    let mut out = vec![0u64; a.len() + b.len() - 1];
420    for (i, &x) in a.iter().enumerate() {
421        if x == 0 {
422            continue;
423        }
424        for (j, &y) in b.iter().enumerate() {
425            out[i + j] = (out[i + j] as u128 + x as u128 * y as u128 % m as u128) as u64 % m;
426        }
427    }
428    trim_u(&mut out);
429    out
430}
431
432/// 二叉树多重 Hensel:f̄(monic,模 p^k 表示的目标)≡ Π G_i (mod p^k),
433/// G_i ≡ g_i (mod p)。叶子顺序与 g_i 一致。
434fn hensel_all(target: &[u64], facs: &[Fp], p: u64, k: u32, pk: u64) -> Vec<Vec<u64>> {
435    if facs.len() == 1 {
436        return vec![target.to_vec()];
437    }
438    let mid = facs.len() / 2;
439    let (left, right) = facs.split_at(mid);
440    let a0 = left
441        .iter()
442        .cloned()
443        .reduce(|x, y| fp_mul(&x, &y, p))
444        .unwrap();
445    let b0 = right
446        .iter()
447        .cloned()
448        .reduce(|x, y| fp_mul(&x, &y, p))
449        .unwrap();
450    let (_, s, t) = fp_xgcd(&a0, &b0, p);
451    let Some((al, br)) = hensel2(target, &a0, &b0, &s, &t, p, k, pk) else {
452        // 保守回退:所有因子之积按 target 返回(等同整体不分解)
453        return vec![target.to_vec(); facs.len()];
454    };
455    let mut out = hensel_all(&al, left, p, k, pk);
456    out.extend(hensel_all(&br, right, p, k, pk));
457    out
458}
459
460// ── Zassenhaus 主流程 ─────────────────────────────────────────
461
462/// 本原 squarefree S(首项正)的不可约因子(ℤ[x],本原、首项正)。
463fn zassenhaus(s: &IPoly) -> Vec<IPoly> {
464    let d = ip_deg(s);
465    if d <= 1 {
466        return vec![s.to_vec()];
467    }
468    let lc = *s.last().unwrap() as i64; // 界内安全
469    let amax: i128 = s.iter().map(|c| c.abs()).max().unwrap();
470    // 选质数:不除尽首项、模 p 后仍 squarefree
471    let mut chosen: Option<(u64, Fp)> = None;
472    for &p in PRIMES {
473        if lc % p as i64 == 0 {
474            continue;
475        }
476        let fmod: Fp = s
477            .iter()
478            .map(|&c| (c.rem_euclid(p as i128)) as u64)
479            .collect();
480        let fmod = fp_monic(fmod, p);
481        let df = fp_deriv(&fmod, p);
482        if fp_gcd(&fmod, &df, p).len() <= 1 {
483            chosen = Some((p, fmod));
484            break;
485        }
486    }
487    let (p, fmod) = match chosen {
488        Some(x) => x,
489        None => {
490            return vec![s.to_vec()]; // 找不到好质数:视为不可约(测试范围外)
491        }
492    };
493    let mut det = Det::new();
494    let facs_p = cz_factor(&fmod, p, &mut det);
495    if facs_p.len() <= 1 {
496        return vec![s.to_vec()]; // 模 p 不可约 ⇒ ℚ 上不可约
497    }
498    // Mignotte 界与提升模数;上限 2^60(u64 乘法中间量安全)——
499    // 超界则保守不分解(多精度 Hensel 属后续工作,见 DESIGN)
500    let b_bound: u128 = (2u128 << d.min(120)) * amax as u128 * lc.unsigned_abs() as u128;
501    let mut pk = p as u128;
502    let mut k = 1u32;
503    while pk <= 2 * b_bound {
504        pk *= p as u128;
505        k += 1;
506        if pk > 1 << 60 {
507            return vec![s.to_vec()];
508        }
509    }
510    let pk = pk as u64;
511    // 目标:f̄ = lc^{-1}·s mod p^k(monic)
512    let lc_inv = inv_mod(lc.rem_euclid(pk as i64) as u64, pk);
513    // 注意 u128 中转:c 与 lc_inv 都可近 pk(≈2^60),u64 乘法静默溢出——
514    // 曾致"整体符号翻转后 Hensel 第一步不变量破坏"(定点复现 seed=2)
515    let a: Vec<u64> = s
516        .iter()
517        .map(|&c| ((c.rem_euclid(pk as i128) as u64 as u128 * lc_inv as u128) % pk as u128) as u64)
518        .collect();
519    let lifted = hensel_all(&a, &facs_p, p, k, pk);
520    // 确定性子集组合
521    combine(s, &lifted, pk, lc)
522}
523
524fn inv_mod(a: u64, m: u64) -> u64 {
525    // 扩展 Euclid(m 不必素数)
526    let (mut r0, mut r1) = (m as i128, a as i128 % m as i128);
527    let (mut s0, mut s1) = (0i128, 1i128);
528    while r1 != 0 {
529        let q = r0 / r1;
530        let r2 = r0 - q * r1;
531        r0 = r1;
532        r1 = r2;
533        let s2 = s0 - q * s1;
534        s0 = s1;
535        s1 = s2;
536    }
537    debug_assert_eq!(r0, 1, "逆元不存在");
538    s0.rem_euclid(m as i128) as u64
539}
540
541/// 子集组合:候选 = Π_{i∈S} G_i mod pk → 对称化 → 本原部分 → 试除。
542fn combine(f: &IPoly, lifted: &[Vec<u64>], pk: u64, lc: i64) -> Vec<IPoly> {
543    let r = lifted.len();
544    if r > 16 {
545        return vec![f.to_vec()]; // 组合爆炸保护(测试范围外)
546    }
547    let half = pk / 2;
548    let sym = |v: Vec<u64>| -> IPoly {
549        v.iter()
550            .map(|&c| {
551                if c > half {
552                    c as i128 - pk as i128
553                } else {
554                    c as i128
555                }
556            })
557            .collect()
558    };
559    let mut factors: Vec<IPoly> = vec![];
560    let mut used = vec![false; r];
561    let mut f_cur = f.clone();
562    // 子集按基数升序枚举:避免把多个真因子的积当成单因子(漏拆)
563    let mut masks: Vec<u64> = (1..(1u64 << (r - 1))).collect();
564    masks.sort_by_key(|m| m.count_ones());
565    loop {
566        let mut found = false;
567        for &mask in &masks {
568            // 跳过已用因子
569            if (0..r - 1).any(|i| mask >> i & 1 == 1 && used[i]) {
570                continue;
571            }
572            if mask.count_ones() as usize > ip_deg(&f_cur).max(0) as usize {
573                continue;
574            }
575            // 候选乘积(模 pk);提升因子是首一的,真因子须乘 lc(f) 后
576            // 再对称化取本原部分(经典 Zassenhaus 组合),同时试 ±两号
577            let mut cand: Vec<u64> = vec![1];
578            for (i, g) in lifted.iter().enumerate().take(r - 1) {
579                if mask >> i & 1 == 1 {
580                    cand = umul(&cand, g, pk);
581                }
582            }
583            // 首项比例自由度:试 lc^j(j = 0..=|S|;|S| 个首一因子的积与
584            // 真因子的首项差 lc 的某次幂——模 p 下因子内部降次时 j 可 >1)
585            let lc_u = (lc as i128).rem_euclid(pk as i128) as u64;
586            let k = mask.count_ones();
587            let mut hit = None;
588            let mut scaled = cand.clone();
589            for j in 0..=k {
590                if j > 0 {
591                    scaled = umul(&scaled, &[lc_u], pk);
592                }
593                let cand_ip = ip_trim_mut(sym(scaled.clone()));
594                if cand_ip.is_empty() {
595                    continue;
596                }
597                let (_, pp) = ip_primitive(&cand_ip);
598                if pp.is_empty() || ip_deg(&pp) == 0 || ip_deg(&pp) > ip_deg(&f_cur) {
599                    continue;
600                }
601                if let Some(q) = ip_exact_div(&f_cur, &pp) {
602                    hit = Some((pp, q));
603                    break;
604                }
605            }
606            if let Some((pp, q)) = hit {
607                factors.push(pp);
608                f_cur = q;
609                for (i, u) in used.iter_mut().enumerate().take(r - 1) {
610                    if mask >> i & 1 == 1 {
611                        *u = true;
612                    }
613                }
614                found = true;
615                break;
616            }
617        }
618        if !found {
619            break;
620        }
621    }
622    if !f_cur.is_empty() && ip_deg(&f_cur) > 0 {
623        factors.push(f_cur);
624    }
625    factors
626}
627
628fn ip_trim_mut(mut v: IPoly) -> IPoly {
629    ip_trim(&mut v);
630    v
631}
632
633// ── Yun 平方自由分解(复用 Poly<Rational> 的 gcd/exact_div/deriv)──
634
635pub(crate) fn squarefree_parts(f: &Poly<Rational>) -> Vec<(Poly<Rational>, u32)> {
636    let df = f.deriv(0);
637    if df.is_zero() {
638        return vec![(f.clone(), 1)];
639    }
640    let c = f.gcd(&df);
641    let mut w = match f.exact_div(&c) {
642        Some(v) => v,
643        None => return vec![(f.clone(), 1)],
644    };
645    let mut z = match df.exact_div(&c) {
646        Some(v) => v.sub(&w.deriv(0)),
647        None => return vec![(f.clone(), 1)],
648    };
649    let mut out = vec![];
650    let mut i = 1u32;
651    while !w.is_constant() {
652        let g = w.gcd(&z);
653        if !g.is_constant() {
654            out.push((g.clone(), i));
655            w = w.exact_div(&g).expect("Yun 整除");
656            z = z.exact_div(&g).expect("Yun 整除").sub(&w.deriv(0));
657        } else {
658            z = z.sub(&w.deriv(0));
659        }
660        i += 1;
661    }
662    out
663}
664
665// ── 公共 API ─────────────────────────────────────────────────
666
667impl Poly<Rational> {
668    /// 一元 ℚ[x] 因式分解:返回(常数内容, [(本原因子, 重数)]),
669    /// 满足 cont · Π 因子^重数 == self。因子本原、首项正;输出确定性。
670    pub fn factor_univariate(&self) -> (Rational, Vec<(Poly<Rational>, u32)>) {
671        assert_eq!(self.ring().nvars(), 1, "factor_univariate 仅支持一元");
672        if self.is_zero() {
673            return (Rational::zero(), vec![]);
674        }
675        if self.is_constant() {
676            return (self.terms().next().unwrap().1.clone(), vec![]);
677        }
678        // 整数本原化
679        let mut den_lcm = Integer::one();
680        for (_, c) in self.terms() {
681            den_lcm = den_lcm.mul(&c.den());
682        }
683        let mut num_gcd = Integer::zero();
684        for (_, c) in self.terms() {
685            let scaled = c.num().mul(&den_lcm.div_exact(&c.den()));
686            num_gcd = if num_gcd.is_zero() {
687                scaled
688            } else {
689                num_gcd.gcd(&scaled)
690            };
691        }
692        if num_gcd.is_zero() {
693            num_gcd = Integer::one();
694        }
695        let cont = Rational::from_ints(&num_gcd, &den_lcm).unwrap();
696        let mut dense: IPoly = {
697            let mut v = vec![0i128; self.degree() as usize + 1];
698            for (e, c) in self.terms() {
699                let scaled = c.mul(&Rational::from_integer(&den_lcm));
700                let r = scaled.div(&Rational::from_integer(&num_gcd)).unwrap();
701                assert!(r.den().is_one(), "本原化后必为整系数");
702                v[e[0] as usize] = r.num().to_i64().expect("本原化后系数落 i64") as i128;
703            }
704            v
705        };
706        // 前置条件:zassenhaus 要求首项正;负号并入内容
707        let mut cont = cont;
708        if *dense.last().unwrap() < 0 {
709            for c in &mut dense {
710                *c = -*c;
711            }
712            cont = cont.neg();
713        }
714        // 重数分解 + Zassenhaus
715        let ring = self.ring().clone();
716        let mut factors: Vec<(Poly<Rational>, u32)> = vec![];
717        for (s, m) in squarefree_parts(&from_dense(&dense, &ring)) {
718            if s.is_constant() {
719                continue;
720            }
721            let dense_s = to_dense(&s);
722            for h in zassenhaus(&dense_s) {
723                factors.push((from_dense(&h, &ring), m));
724            }
725        }
726        // 确定性排序:次数升序,同次按系数字典序
727        factors.sort_by(|a, b| cmp_dense(&to_dense(&a.0), &to_dense(&b.0)));
728        (cont, factors)
729    }
730}
731
732fn to_dense(p: &Poly<Rational>) -> IPoly {
733    let mut v = vec![0i128; p.degree() as usize + 1];
734    for (e, c) in p.terms() {
735        assert!(c.den().is_one(), "to_dense 需整系数");
736        v[e[0] as usize] = c.num().to_i64().expect("整系数落 i64") as i128;
737    }
738    v
739}
740
741fn from_dense(v: &IPoly, ring: &Arc<crate::PolyRing>) -> Poly<Rational> {
742    let items: Vec<(Vec<u32>, Rational)> = v
743        .iter()
744        .enumerate()
745        .filter(|(_, c)| **c != 0)
746        .map(|(i, &c)| {
747            (
748                vec![i as u32],
749                Rational::from_integer(&Integer::from_i64(c as i64)),
750            )
751        })
752        .collect();
753    Poly::from_terms(ring.clone(), items)
754}
755
756fn cmp_dense(a: &IPoly, b: &IPoly) -> std::cmp::Ordering {
757    use std::cmp::Ordering;
758    match ip_deg(a).cmp(&ip_deg(b)) {
759        Ordering::Equal => {}
760        o => return o,
761    }
762    for i in (0..a.len()).rev() {
763        match a[i].cmp(&b[i]) {
764            Ordering::Equal => {}
765            o => return o,
766        }
767    }
768    std::cmp::Ordering::Equal
769}
770
771#[cfg(test)]
772mod tests {
773    use super::*;
774    use crate::{MonOrder, PolyRing};
775
776    fn ring() -> Arc<PolyRing> {
777        PolyRing::new(["x"], MonOrder::DegRevLex)
778    }
779
780    fn from_coeffs(cs: &[i64]) -> Poly<Rational> {
781        let items: Vec<(Vec<u32>, Rational)> = cs
782            .iter()
783            .enumerate()
784            .filter(|(_, c)| **c != 0)
785            .map(|(i, &c)| {
786                (
787                    vec![i as u32],
788                    Rational::from_ints(&Integer::from_i64(c), &Integer::from_i64(1)).unwrap(),
789                )
790            })
791            .collect();
792        Poly::from_terms(ring(), items)
793    }
794
795    fn check_refold(f: &Poly<Rational>) {
796        let (cont, facs) = f.factor_univariate();
797        let mut prod = Poly::constant(ring(), cont);
798        for (g, m) in &facs {
799            prod = prod.mul(&g.pow(*m));
800        }
801        assert_eq!(prod, *f, "重展开不等于原式: f={f:?} factors={facs:?}");
802    }
803
804    #[test]
805    fn 二项式与已知分解() {
806        let f = from_coeffs(&[-1, 0, 1]); // x^2 - 1
807        let (c, facs) = f.factor_univariate();
808        assert_eq!(facs.len(), 2);
809        let mut prod = Poly::constant(ring(), c);
810        for (g, m) in &facs {
811            prod = prod.mul(&g.pow(*m));
812        }
813        assert_eq!(prod, f);
814
815        // x^4 + 4 = (x² − 2x + 2)(x² + 2x + 2)
816        let f = from_coeffs(&[4, 0, 0, 0, 1]);
817        let (_, facs) = f.factor_univariate();
818        assert_eq!(facs.len(), 2, "x^4+4 应分解为两个二次因子: {facs:?}");
819        check_refold(&f);
820
821        // x^4 + 1 在 ℚ 上不可约
822        let f = from_coeffs(&[1, 0, 0, 0, 1]);
823        let (_, facs) = f.factor_univariate();
824        assert_eq!(facs.len(), 1);
825    }
826
827    #[test]
828    fn x_n_minus_1_全族() {
829        for n in 1..=64u32 {
830            let mut cs = vec![0i64; n as usize + 1];
831            cs[0] = -1;
832            cs[n as usize] = 1;
833            let f = from_coeffs(&cs);
834            check_refold(&f);
835        }
836        // x^12 − 1 有 6 个不可约因子(分圆多项式个数 = d|12 的 φ(d)>0 的因子)
837        let mut cs = vec![0i64; 13];
838        cs[0] = -1;
839        cs[12] = 1;
840        let (_, facs) = from_coeffs(&cs).factor_univariate();
841        assert_eq!(facs.len(), 6);
842    }
843
844    #[test]
845    fn 随机积重展开() {
846        let mut det = Det::new();
847        for _ in 0..600 {
848            let nf = 2 + det.next() % 2; // 2–3 个因子
849            let mut f = Poly::constant(ring(), Rational::one());
850            for _ in 0..nf {
851                let deg = 1 + det.next() % 6;
852                let cs: Vec<i64> = (0..=deg).map(|_| (det.next() % 13) as i64 - 6).collect();
853                let g = from_coeffs(&cs);
854                if g.is_zero() {
855                    continue;
856                }
857                f = f.mul(&g);
858            }
859            // 有理内容
860            let scale = Rational::from_ints(
861                &Integer::from_i64((det.next() % 7) as i64 - 3),
862                &Integer::from_i64(1 + (det.next() % 5) as i64),
863            )
864            .unwrap();
865            let f = f.mul(&Poly::constant(ring(), scale));
866            if !f.is_zero() && !f.is_constant() {
867                check_refold(&f);
868            }
869        }
870    }
871
872    #[test]
873    fn 重数与内容() {
874        // (2x − 2)^3 = 2^3 (x − 1)^3
875        let f = from_coeffs(&[-8, 24, -24, 8]);
876        let (c, facs) = f.factor_univariate();
877        assert_eq!(facs.len(), 1);
878        assert_eq!(facs[0].1, 3);
879        assert_eq!(
880            c,
881            Rational::from_ints(&Integer::from_i64(8), &Integer::from_i64(1)).unwrap()
882        );
883        check_refold(&f);
884    }
885}
886
887#[cfg(test)]
888mod hensel_regression {
889    use super::*;
890
891    /// u64 溢出回归:整体系数符号翻转后 Hensel 第一步不变量破坏
892    /// (lc⁻¹·c 在 u64 下静默回绕;修复为 u128 中转)。
893    #[test]
894    fn 溢出回归_符号翻转() {
895        let f1: IPoly = vec![-1, 2, 5, 2, -6];
896        let f2: IPoly = vec![6, -3, 1, 1, 6, 3, -4];
897        let f3: IPoly = vec![4, -6, 0, -1, -5];
898        let mut f = vec![0i128; 15];
899        for (i, &x) in f1.iter().enumerate() {
900            for (j, &y) in f2.iter().enumerate() {
901                for (k, &z) in f3.iter().enumerate() {
902                    f[i + j + k] += x * y * z;
903                }
904            }
905        }
906        ip_trim(&mut f);
907        assert_eq!(zassenhaus(&f).len(), 3, "原始版本");
908        let mut neg = f.clone();
909        for c in &mut neg {
910            *c = -*c;
911        }
912        assert_eq!(zassenhaus(&neg).len(), 3, "符号翻转版本(曾触发 u64 溢出)");
913    }
914}