use super::error::MatGroupError;
use super::MAX_MATGROUP_FIELD_ORDER;
use crate::ffield::{FieldElement, FiniteField, GfMatrix};
pub(crate) type EntryKey = Vec<u64>;
pub(crate) fn entry_key(field: &FiniteField, m: &GfMatrix) -> EntryKey {
let k = field.degree();
let elements = m.to_elements();
let mut out = Vec::with_capacity(elements.len() * k);
for e in &elements {
out.extend(e.coefficients_padded(k));
}
out
}
pub(crate) fn field_order_u64(field: &FiniteField) -> Result<u64, MatGroupError> {
let q = field.order().ok_or_else(|| MatGroupError::FieldTooLarge {
order: format!("{}^{}", field.characteristic(), field.degree()),
max: MAX_MATGROUP_FIELD_ORDER,
})?;
if q > u128::from(MAX_MATGROUP_FIELD_ORDER) {
return Err(MatGroupError::FieldTooLarge {
order: q.to_string(),
max: MAX_MATGROUP_FIELD_ORDER,
});
}
Ok(q as u64)
}
pub(crate) fn field_elements(field: &FiniteField) -> Result<Vec<FieldElement>, MatGroupError> {
let q = field_order_u64(field)?;
let p = field.characteristic();
let k = field.degree();
let mut out = Vec::with_capacity(q as usize);
for code in 0..q {
let mut rest = code;
let mut coeffs = Vec::with_capacity(k);
for _ in 0..k {
coeffs.push(rest % p);
rest /= p;
}
out.push(field.element(&coeffs)?);
}
Ok(out)
}
fn scalar_matrix(field: &FiniteField, a: &FieldElement) -> Result<GfMatrix, MatGroupError> {
Ok(GfMatrix::from_elements(
field,
1,
1,
std::slice::from_ref(a),
)?)
}
fn only_element(m: &GfMatrix) -> Result<FieldElement, MatGroupError> {
m.to_elements()
.into_iter()
.next()
.ok_or(MatGroupError::Internal {
detail: "a 1x1 matrix with no entry",
})
}
pub(crate) fn fe_mul(
field: &FiniteField,
a: &FieldElement,
b: &FieldElement,
) -> Result<FieldElement, MatGroupError> {
let prod = scalar_matrix(field, a)?.mul(&scalar_matrix(field, b)?)?;
only_element(&prod)
}
pub(crate) fn fe_sub(
field: &FiniteField,
a: &FieldElement,
b: &FieldElement,
) -> Result<FieldElement, MatGroupError> {
let diff = scalar_matrix(field, a)?.sub(&scalar_matrix(field, b)?)?;
only_element(&diff)
}
pub(crate) fn fe_inverse(
field: &FiniteField,
a: &FieldElement,
) -> Result<FieldElement, MatGroupError> {
let inv = scalar_matrix(field, a)?.inverse()?;
only_element(&inv)
}
pub(crate) fn primitive_element(field: &FiniteField) -> Result<FieldElement, MatGroupError> {
let q = field_order_u64(field)?;
let one = field.one();
for cand in field_elements(field)? {
if cand.is_zero() {
continue;
}
let mut x = cand.clone();
let mut order = 1u64;
while x != one {
x = fe_mul(field, &x, &cand)?;
order += 1;
if order > q {
return Err(MatGroupError::Internal {
detail: "an element of GF(q)* has multiplicative order above q",
});
}
}
if order == q - 1 {
return Ok(cand);
}
}
Err(MatGroupError::Internal {
detail: "GF(q)* is cyclic but no generator was found",
})
}
pub(crate) fn basis_vector(
field: &FiniteField,
degree: usize,
i: usize,
) -> Result<GfMatrix, MatGroupError> {
if i >= degree {
return Err(MatGroupError::DegreeMismatch {
expected: degree,
got: i + 1,
});
}
let mut entries = vec![field.zero(); degree];
entries[i] = field.one();
Ok(GfMatrix::from_elements(field, 1, degree, &entries)?)
}
pub(crate) fn row_vector(
field: &FiniteField,
m: &GfMatrix,
i: usize,
) -> Result<GfMatrix, MatGroupError> {
let cols = m.ncols();
let elements = m.to_elements();
let start = i * cols;
let slice = elements
.get(start..start + cols)
.ok_or(MatGroupError::Internal {
detail: "row index outside a matrix whose shape was already checked",
})?;
Ok(GfMatrix::from_elements(field, 1, cols, slice)?)
}
pub(crate) fn is_identity(field: &FiniteField, m: &GfMatrix) -> bool {
let (rows, cols) = m.shape();
if rows != cols {
return false;
}
let one = field.one();
let elements = m.to_elements();
for i in 0..rows {
for j in 0..cols {
let e = &elements[i * cols + j];
if i == j {
if *e != one {
return false;
}
} else if !e.is_zero() {
return false;
}
}
}
true
}
pub(crate) fn first_moved_basis_vector(field: &FiniteField, m: &GfMatrix) -> Option<usize> {
let (rows, cols) = m.shape();
if rows != cols {
return None;
}
let one = field.one();
let elements = m.to_elements();
for i in 0..rows {
for j in 0..cols {
let e = &elements[i * cols + j];
let expected_one = i == j;
if (expected_one && *e != one) || (!expected_one && !e.is_zero()) {
return Some(i);
}
}
}
None
}
pub(crate) fn projective_normalise(
field: &FiniteField,
v: &GfMatrix,
) -> Result<GfMatrix, MatGroupError> {
let elements = v.to_elements();
let pivot = elements
.iter()
.find(|e| !e.is_zero())
.ok_or(MatGroupError::ZeroVector)?;
if *pivot == field.one() {
return Ok(v.clone());
}
let scale = fe_inverse(field, pivot)?;
Ok(v.scalar_mul(&scale)?)
}
pub(crate) fn conjugate(g: &GfMatrix, h: &GfMatrix) -> Result<GfMatrix, MatGroupError> {
Ok(g.inverse()?.mul(h)?.mul(g)?)
}
pub(crate) fn commutator(g: &GfMatrix, h: &GfMatrix) -> Result<GfMatrix, MatGroupError> {
Ok(g.inverse()?.mul(&h.inverse()?)?.mul(g)?.mul(h)?)
}
pub(crate) fn commutes(a: &GfMatrix, b: &GfMatrix) -> Result<bool, MatGroupError> {
Ok(a.mul(b)?.equals(&b.mul(a)?))
}