hop-core 0.1.0

HOP core — finite-field companion-matrix stream primitives (prototype)
Documentation
use crate::modules::field::Field;

/// Companion matrix utilities (dense matrix representation)
/// Matrices are represented as Vec<Vec<Field>> in row-major order.
pub type Mat = Vec<Vec<Field>>;
pub type VecF = Vec<Field>;

/// Build companion matrix for polynomial: x^k + c_{k-1} x^{k-1} + ... + c_0
/// Input: coeffs = [c_0, c_1, ..., c_{k-1}] corresponding to recurrence
/// Companion here corresponds to state update for vector [F_n, F_{n-1}, ..., F_{n-k+1}]^T
pub struct CompanionMatrix {
    pub k: usize,
    pub mat: Mat,
}

impl CompanionMatrix {
    pub fn from_coeffs(coeffs: &[u64]) -> Self {
        let k = coeffs.len();
        let mut m: Mat = vec![vec![Field::zero(); k]; k];
        // superdiagonal ones
        for i in 0..(k-1) {
            m[i][i+1] = Field::one();
        }
        // last row: negative coefficients (to match companion convention)
        for j in 0..k {
            // store as -coeffs[k-1-j] because of ordering mapping if needed;
            // We assume coeffs provided in order [a1, a2, ..., ak] with recurrence
            // F_n = a1 F_{n-1} + ... + ak F_{n-k}
            // companion last row should be [-ak, -a_{k-1}, ..., -a1] if state is [F_n, F_{n-1},...]
            let c = coeffs[j];
            // store negative c at last row, column j
            let neg = Field::new((Field::MOD as u128 - (c as u128 % Field::MOD as u128)) % (Field::MOD as u128));
            m[k-1][j] = neg;
        }
        CompanionMatrix { k, mat: m }
    }

    pub fn mat_vec_mul(&self, v: &VecF) -> VecF {
        assert_eq!(v.len(), self.k);
        let mut out = vec![Field::zero(); self.k];
        for i in 0..self.k {
            let mut acc = Field::zero();
            for j in 0..self.k {
                acc = acc.add(self.mat[i][j].mul(v[j]));
            }
            out[i] = acc;
        }
        out
    }

    pub fn mat_mul(&self, other: &Mat) -> Mat {
        assert_eq!(other.len(), self.k);
        let mut res = vec![vec![Field::zero(); self.k]; self.k];
        for i in 0..self.k {
            for j in 0..self.k {
                let mut acc = Field::zero();
                for t in 0..self.k {
                    acc = acc.add(self.mat[i][t].mul(other[t][j]));
                }
                res[i][j] = acc;
            }
        }
        res
    }

    pub fn mat_pow(&self, mut exp: u64) -> Mat {
        // exponentiate the matrix self.mat to power exp
        // binary exponentiation
        let mut base = self.mat.clone();
        let mut res = identity(self.k);
        while exp > 0 {
            if exp & 1 == 1 {
                res = multiply_mat(&res, &base);
            }
            base = multiply_mat(&base, &base);
            exp >>= 1;
        }
        res
    }
}

pub fn identity(k: usize) -> Mat {
    let mut id = vec![vec![Field::zero(); k]; k];
    for i in 0..k { id[i][i] = Field::one(); }
    id
}

pub fn multiply_mat(a: &Mat, b: &Mat) -> Mat {
    let k = a.len();
    let mut res = vec![vec![Field::zero(); k]; k];
    for i in 0..k {
        for j in 0..k {
            let mut acc = Field::zero();
            for t in 0..k { acc = acc.add(a[i][t].mul(b[t][j])); }
            res[i][j] = acc;
        }
    }
    res
}