Skip to main content

logicaffeine_proof/
macaulay.rs

1//! Monomials and the Macaulay matrix — the combinatorial skeleton of the XL algorithm.
2//!
3//! XL ("eXtended Linearization", Courtois–Klimov–Patarin–Shamir 2000) solves a system of
4//! multivariate polynomial equations by *linearizing*: multiply every equation by every
5//! monomial up to a chosen degree, treat each distinct monomial as an independent unknown,
6//! and solve the resulting linear system. This is the engine behind Gröbner-basis-style
7//! algebraic cryptanalysis of multivariate schemes (Rainbow / Oil-and-Vinegar / HFE) — the
8//! attack surface Prof. Ding formalized through the *degree of regularity*.
9//!
10//! This module owns only the field-agnostic bookkeeping: how monomials are represented,
11//! enumerated up to a degree bound, and multiplied. The field arithmetic and the linear
12//! solve live in [`crate::xl`].
13//!
14//! A monomial is the **sorted multiset of its variable indices** — `[]` is the constant `1`,
15//! `[i]` is `x_i`, `[i, i]` is `x_i²`, `[i, j]` (i ≤ j) is `x_i x_j`. The degree is the length.
16//! In a multilinear ring (GF(2), where `x² = x`) monomials are squarefree (strictly
17//! increasing); otherwise repeats encode true exponents.
18
19/// A monomial: the sorted multiset of variable indices it contains. `Vec::new()` is `1`.
20pub type Monomial = Vec<u32>;
21
22/// The constant monomial `1`.
23pub fn one() -> Monomial {
24    Vec::new()
25}
26
27/// The degree of a monomial (its total number of variable factors).
28pub fn degree(m: &Monomial) -> usize {
29    m.len()
30}
31
32/// The product of two monomials. When `multilinear`, repeated variables collapse (`x² = x`),
33/// the correct rule for GF(2); otherwise exponents accumulate.
34pub fn mul(a: &Monomial, b: &Monomial, multilinear: bool) -> Monomial {
35    let mut m: Monomial = a.iter().chain(b.iter()).copied().collect();
36    m.sort_unstable();
37    if multilinear {
38        m.dedup();
39    }
40    m
41}
42
43/// Every monomial in `n` variables of degree exactly `deg`, listed with variable indices in
44/// nondecreasing order. Strictly increasing (squarefree) when `multilinear`.
45pub fn monomials_of_degree(n: u32, deg: usize, multilinear: bool) -> Vec<Monomial> {
46    fn rec(
47        start: u32,
48        n: u32,
49        remaining: usize,
50        multilinear: bool,
51        cur: &mut Monomial,
52        out: &mut Vec<Monomial>,
53    ) {
54        if remaining == 0 {
55            out.push(cur.clone());
56            return;
57        }
58        for v in start..n {
59            cur.push(v);
60            let next = if multilinear { v + 1 } else { v };
61            rec(next, n, remaining - 1, multilinear, cur, out);
62            cur.pop();
63        }
64    }
65    let mut out = Vec::new();
66    let mut cur = Vec::new();
67    rec(0, n, deg, multilinear, &mut cur, &mut out);
68    out
69}
70
71/// Every monomial in `n` variables of degree `0..=max_deg`. The constant `1` is always first
72/// (index `0`), which the XL solver relies on to read off constant terms.
73pub fn monomials_up_to_degree(n: u32, max_deg: usize, multilinear: bool) -> Vec<Monomial> {
74    let mut out = vec![one()];
75    for deg in 1..=max_deg {
76        out.extend(monomials_of_degree(n, deg, multilinear));
77    }
78    out
79}
80
81#[cfg(test)]
82#[path = "../../logicaffeine_tests/unit/logicaffeine_proof/macaulay/tests.rs"]
83mod tests;