use crate::field::Elem;
use crate::kernel::{FieldKernels, KernelDispatch, RawDispatch};
use super::{PreparedCoefficient, PreparedRepr, private};
pub struct Coeff<F: FieldKernels> {
prepared: <F as KernelDispatch>::Prepared,
}
impl<F: FieldKernels> Coeff<F> {
#[inline]
#[must_use]
pub fn new(coeff: Elem<F>) -> Self {
Self {
prepared: F::prepare(RawDispatch, coeff),
}
}
#[inline]
#[must_use]
pub fn value(&self) -> Elem<F> {
F::prepared_coeff(RawDispatch, &self.prepared)
}
}
impl<F: FieldKernels> Clone for Coeff<F> {
fn clone(&self) -> Self {
Self {
prepared: self.prepared.clone(),
}
}
}
impl<F: FieldKernels> core::fmt::Debug for Coeff<F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("Coeff").field(&self.value()).finish()
}
}
impl<F: FieldKernels> private::Sealed for Coeff<F> {}
impl<F: FieldKernels> PreparedCoefficient<F> for Coeff<F> {
#[inline]
fn value(&self) -> Elem<F> {
Coeff::value(self)
}
}
impl<F: FieldKernels> PreparedRepr<F> for Coeff<F> {
#[inline]
fn repr(&self, _proof: RawDispatch) -> &<F as KernelDispatch>::Prepared {
&self.prepared
}
}
#[cfg(feature = "alloc")]
#[derive(Clone, Copy)]
pub struct CoeffRef<'a, F: FieldKernels> {
prepared: &'a <F as KernelDispatch>::Prepared,
field: core::marker::PhantomData<F>,
}
#[cfg(feature = "alloc")]
impl<F: FieldKernels> CoeffRef<'_, F> {
#[inline]
#[must_use]
pub fn value(self) -> Elem<F> {
F::prepared_coeff(RawDispatch, self.prepared)
}
}
#[cfg(feature = "alloc")]
impl<F: FieldKernels> private::Sealed for CoeffRef<'_, F> {}
#[cfg(feature = "alloc")]
impl<F: FieldKernels> PreparedCoefficient<F> for CoeffRef<'_, F> {
#[inline]
fn value(&self) -> Elem<F> {
F::prepared_coeff(RawDispatch, self.prepared)
}
}
#[cfg(feature = "alloc")]
impl<F: FieldKernels> PreparedRepr<F> for CoeffRef<'_, F> {
#[inline]
fn repr(&self, _proof: RawDispatch) -> &<F as KernelDispatch>::Prepared {
self.prepared
}
}
#[cfg(feature = "alloc")]
impl<F: FieldKernels> core::fmt::Debug for CoeffRef<'_, F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_tuple("CoeffRef").field(&self.value()).finish()
}
}
#[cfg(feature = "alloc")]
pub struct CoeffVec<F: FieldKernels> {
prepared: alloc::boxed::Box<[<F as KernelDispatch>::Prepared]>,
values: alloc::boxed::Box<[Elem<F>]>,
}
#[cfg(feature = "alloc")]
impl<F: FieldKernels> CoeffVec<F> {
#[must_use]
pub fn new(coeffs: &[Elem<F>]) -> Self {
Self {
prepared: prepare_all::<F>(coeffs),
values: alloc::boxed::Box::from(coeffs),
}
}
#[inline]
#[must_use]
pub fn as_ref(&self) -> CoeffVecRef<'_, F> {
CoeffVecRef {
prepared: &self.prepared,
values: &self.values,
field: core::marker::PhantomData,
}
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.prepared.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.prepared.is_empty()
}
#[inline]
#[must_use]
pub fn get(&self, index: usize) -> Option<CoeffRef<'_, F>> {
self.as_ref().get(index)
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub fn coeffs(&self) -> impl ExactSizeIterator<Item = CoeffRef<'_, F>> {
self.as_ref().into_coeffs()
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub fn values(&self) -> impl ExactSizeIterator<Item = Elem<F>> + '_ {
self.values.iter().copied()
}
}
#[cfg(feature = "alloc")]
impl<F: FieldKernels> Clone for CoeffVec<F> {
fn clone(&self) -> Self {
Self {
prepared: self.prepared.clone(),
values: self.values.clone(),
}
}
}
#[cfg(feature = "alloc")]
impl<F: FieldKernels> core::fmt::Debug for CoeffVec<F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("CoeffVec")
.field("len", &self.len())
.finish_non_exhaustive()
}
}
#[cfg(feature = "alloc")]
#[derive(Clone, Copy)]
pub struct CoeffVecRef<'a, F: FieldKernels> {
pub(super) prepared: &'a [<F as KernelDispatch>::Prepared],
pub(super) values: &'a [Elem<F>],
field: core::marker::PhantomData<F>,
}
#[cfg(feature = "alloc")]
impl<'a, F: FieldKernels> CoeffVecRef<'a, F> {
#[inline]
#[must_use]
pub fn len(self) -> usize {
self.prepared.len()
}
#[inline]
#[must_use]
pub fn is_empty(self) -> bool {
self.prepared.is_empty()
}
#[inline]
#[must_use]
pub fn get(self, index: usize) -> Option<CoeffRef<'a, F>> {
self.prepared.get(index).map(|prepared| CoeffRef {
prepared,
field: core::marker::PhantomData,
})
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub fn into_coeffs(self) -> impl ExactSizeIterator<Item = CoeffRef<'a, F>> {
self.prepared.iter().map(|prepared| CoeffRef {
prepared,
field: core::marker::PhantomData,
})
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub fn values(self) -> impl ExactSizeIterator<Item = Elem<F>> + 'a {
self.values.iter().copied()
}
}
#[cfg(feature = "alloc")]
impl<F: FieldKernels> core::fmt::Debug for CoeffVecRef<'_, F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("CoeffVecRef")
.field("len", &self.len())
.finish_non_exhaustive()
}
}
#[cfg(feature = "alloc")]
pub struct CoeffMatrix<F: FieldKernels> {
pub(super) prepared: alloc::boxed::Box<[<F as KernelDispatch>::Prepared]>,
pub(super) values: alloc::boxed::Box<[Elem<F>]>,
sources: usize,
outputs: usize,
}
#[cfg(feature = "alloc")]
impl<F: FieldKernels> CoeffMatrix<F> {
#[must_use]
pub fn from_source_major(sources: usize, outputs: usize, coeffs: &[Elem<F>]) -> Self {
let len = sources
.checked_mul(outputs)
.expect("CoeffMatrix::from_source_major: dimensions overflow");
assert_eq!(
coeffs.len(),
len,
"CoeffMatrix::from_source_major: {} coefficients for {sources}x{outputs} source-major matrix",
coeffs.len()
);
Self {
prepared: prepare_all::<F>(coeffs),
values: alloc::boxed::Box::from(coeffs),
sources,
outputs,
}
}
#[inline]
#[must_use]
pub const fn source_count(&self) -> usize {
self.sources
}
#[inline]
#[must_use]
pub const fn output_count(&self) -> usize {
self.outputs
}
#[inline]
#[must_use]
pub fn len(&self) -> usize {
self.prepared.len()
}
#[inline]
#[must_use]
pub fn is_empty(&self) -> bool {
self.prepared.is_empty()
}
#[inline]
#[must_use]
pub fn get_at(&self, source: usize, output: usize) -> Option<CoeffRef<'_, F>> {
self.source(source)?.get(output)
}
#[must_use]
pub fn source(&self, source: usize) -> Option<CoeffVecRef<'_, F>> {
if source >= self.sources {
return None;
}
let start = source * self.outputs;
let end = start + self.outputs;
Some(CoeffVecRef {
prepared: self.prepared.get(start..end)?,
values: self.values.get(start..end)?,
field: core::marker::PhantomData,
})
}
#[must_use = "iterators are lazy and do nothing unless consumed"]
pub fn values(&self) -> impl ExactSizeIterator<Item = Elem<F>> + '_ {
self.values.iter().copied()
}
}
#[cfg(feature = "alloc")]
impl<F: FieldKernels> Clone for CoeffMatrix<F> {
fn clone(&self) -> Self {
Self {
prepared: self.prepared.clone(),
values: self.values.clone(),
sources: self.sources,
outputs: self.outputs,
}
}
}
#[cfg(feature = "alloc")]
impl<F: FieldKernels> core::fmt::Debug for CoeffMatrix<F> {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("CoeffMatrix")
.field("sources", &self.sources)
.field("outputs", &self.outputs)
.field("len", &self.len())
.finish_non_exhaustive()
}
}
#[cfg(feature = "alloc")]
fn prepare_all<F: FieldKernels>(
coeffs: &[Elem<F>],
) -> alloc::boxed::Box<[<F as KernelDispatch>::Prepared]> {
coeffs
.iter()
.copied()
.map(|coeff| F::prepare(RawDispatch, coeff))
.collect::<alloc::vec::Vec<_>>()
.into_boxed_slice()
}
#[cfg(all(test, feature = "alloc"))]
mod tests {
use super::*;
use crate::field::poly::AES;
use crate::field::{Elem as E8, FiniteField, Polynomial};
use alloc::vec::Vec;
#[test]
fn matrix_source_lookup_is_total() {
let matrix = CoeffMatrix::<FiniteField<2, 8, Polynomial<AES>>>::from_source_major(
3,
2,
&[E8::<FiniteField<2, 8, Polynomial<AES>>>::from_raw(1); 6],
);
assert!(matrix.source(0).is_some());
assert!(matrix.source(2).is_some());
assert!(matrix.source(3).is_none());
assert!(matrix.source(usize::MAX).is_none());
assert_eq!(matrix.source(1).map(CoeffVecRef::len), Some(2));
let empty = CoeffMatrix::<FiniteField<2, 8, Polynomial<AES>>>::from_source_major(3, 0, &[]);
assert_eq!(empty.source_count(), 3);
assert_eq!(empty.output_count(), 0);
assert_eq!(empty.source(0).map(CoeffVecRef::len), Some(0));
assert_eq!(empty.source(2).map(CoeffVecRef::len), Some(0));
assert!(empty.source(3).is_none());
assert!(empty.source(usize::MAX).is_none());
assert!(matrix.get_at(0, usize::MAX).is_none());
assert!(matrix.get_at(usize::MAX, 0).is_none());
assert!(matrix.get_at(usize::MAX, usize::MAX).is_none());
}
#[test]
fn coeff_matrix_is_source_major() {
let values: Vec<E8<FiniteField<2, 8, Polynomial<AES>>>> = (0u8..6)
.map(|i| E8::<FiniteField<2, 8, Polynomial<AES>>>::from_raw(i * 37))
.collect();
let matrix =
CoeffMatrix::<FiniteField<2, 8, Polynomial<AES>>>::from_source_major(2, 3, &values);
for source in 0..2 {
for output in 0..3 {
let at = matrix
.get_at(source, output)
.map(CoeffRef::value)
.expect("in-bounds coordinate");
assert_eq!(at, values[source * 3 + output]);
}
}
let row_one: Vec<E8<FiniteField<2, 8, Polynomial<AES>>>> = matrix
.source(1)
.unwrap()
.into_coeffs()
.map(CoeffRef::value)
.collect();
assert_eq!(row_one, values[3..6]);
}
}