use core::marker::PhantomData;
use serde::{Deserialize, Serialize};
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum BaseEntry {
Preprocessed { offset: usize },
Main { offset: usize },
Periodic,
Public,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum ExtEntry {
Permutation { offset: usize },
Challenge,
PermutationValue,
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct SymbolicVariable<F> {
pub entry: BaseEntry,
pub index: usize,
#[serde(skip)]
pub(crate) _phantom: PhantomData<F>,
}
impl<F> SymbolicVariable<F> {
pub const fn new(entry: BaseEntry, index: usize) -> Self {
Self {
entry,
index,
_phantom: PhantomData,
}
}
pub const fn degree_multiple(&self) -> usize {
match self.entry {
BaseEntry::Preprocessed { .. } | BaseEntry::Main { .. } | BaseEntry::Periodic => 1,
BaseEntry::Public => 0,
}
}
pub fn poly_degree(&self, trace_len: usize, periodic_periods: &[usize]) -> usize {
match self.entry {
BaseEntry::Preprocessed { .. } | BaseEntry::Main { .. } => trace_len.saturating_sub(1),
BaseEntry::Periodic => {
let period = periodic_periods
.get(self.index)
.copied()
.unwrap_or(trace_len)
.min(trace_len);
match period {
0 => trace_len.saturating_sub(1),
p => trace_len - trace_len / p,
}
}
BaseEntry::Public => 0,
}
}
}
#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
pub struct SymbolicVariableExt<F, EF> {
pub entry: ExtEntry,
pub index: usize,
#[serde(skip)]
pub(crate) _phantom: PhantomData<(F, EF)>,
}
impl<F, EF> SymbolicVariableExt<F, EF> {
pub const fn new(entry: ExtEntry, index: usize) -> Self {
Self {
entry,
index,
_phantom: PhantomData,
}
}
pub const fn degree_multiple(&self) -> usize {
match self.entry {
ExtEntry::Permutation { .. } => 1,
ExtEntry::Challenge | ExtEntry::PermutationValue => 0,
}
}
pub const fn poly_degree(&self, trace_len: usize) -> usize {
match self.entry {
ExtEntry::Permutation { .. } => trace_len.saturating_sub(1),
ExtEntry::Challenge | ExtEntry::PermutationValue => 0,
}
}
}
#[cfg(test)]
mod tests {
use p3_baby_bear::BabyBear;
use p3_field::extension::BinomialExtensionField;
use super::*;
type F = BabyBear;
type EF = BinomialExtensionField<F, 4>;
#[test]
fn symbolic_variable_new_main() {
let var = SymbolicVariable::<F>::new(BaseEntry::Main { offset: 1 }, 3);
assert_eq!(var.entry, BaseEntry::Main { offset: 1 });
assert_eq!(var.index, 3);
}
#[test]
fn symbolic_variable_new_preprocessed() {
let var = SymbolicVariable::<F>::new(BaseEntry::Preprocessed { offset: 0 }, 5);
assert_eq!(var.entry, BaseEntry::Preprocessed { offset: 0 });
assert_eq!(var.index, 5);
}
#[test]
fn symbolic_variable_new_public() {
let var = SymbolicVariable::<F>::new(BaseEntry::Public, 2);
assert_eq!(var.entry, BaseEntry::Public);
assert_eq!(var.index, 2);
}
#[test]
fn symbolic_variable_degree_multiple_main() {
let var = SymbolicVariable::<F>::new(BaseEntry::Main { offset: 0 }, 0);
assert_eq!(var.degree_multiple(), 1);
}
#[test]
fn symbolic_variable_degree_multiple_preprocessed() {
let var = SymbolicVariable::<F>::new(BaseEntry::Preprocessed { offset: 0 }, 0);
assert_eq!(var.degree_multiple(), 1);
}
#[test]
fn symbolic_variable_degree_multiple_public() {
let var = SymbolicVariable::<F>::new(BaseEntry::Public, 0);
assert_eq!(var.degree_multiple(), 0);
}
#[test]
fn symbolic_variable_ext_new_permutation() {
let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Permutation { offset: 1 }, 7);
assert_eq!(var.entry, ExtEntry::Permutation { offset: 1 });
assert_eq!(var.index, 7);
}
#[test]
fn symbolic_variable_ext_new_challenge() {
let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Challenge, 4);
assert_eq!(var.entry, ExtEntry::Challenge);
assert_eq!(var.index, 4);
}
#[test]
fn symbolic_variable_poly_degree_main_and_preprocessed() {
let main = SymbolicVariable::<F>::new(BaseEntry::Main { offset: 1 }, 0);
let prep = SymbolicVariable::<F>::new(BaseEntry::Preprocessed { offset: 0 }, 0);
assert_eq!(main.poly_degree(8, &[]), 7);
assert_eq!(prep.poly_degree(8, &[]), 7);
}
#[test]
fn symbolic_variable_poly_degree_public_is_constant() {
let var = SymbolicVariable::<F>::new(BaseEntry::Public, 0);
assert_eq!(var.poly_degree(8, &[]), 0);
}
#[test]
fn symbolic_variable_poly_degree_periodic() {
let var = SymbolicVariable::<F>::new(BaseEntry::Periodic, 0);
assert_eq!(var.poly_degree(8, &[2]), 4);
assert_eq!(var.poly_degree(8, &[4]), 6);
assert_eq!(var.poly_degree(8, &[8]), 7);
assert_eq!(var.poly_degree(8, &[1]), 0);
}
#[test]
fn symbolic_variable_poly_degree_periodic_missing_period_is_conservative() {
let var = SymbolicVariable::<F>::new(BaseEntry::Periodic, 3);
assert_eq!(var.poly_degree(8, &[]), 7);
}
#[test]
fn symbolic_variable_ext_poly_degree() {
let perm = SymbolicVariableExt::<F, EF>::new(ExtEntry::Permutation { offset: 0 }, 0);
let challenge = SymbolicVariableExt::<F, EF>::new(ExtEntry::Challenge, 0);
assert_eq!(perm.poly_degree(8), 7);
assert_eq!(challenge.poly_degree(8), 0);
}
#[test]
fn symbolic_variable_ext_degree_multiple_permutation() {
let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Permutation { offset: 0 }, 0);
assert_eq!(var.degree_multiple(), 1);
}
#[test]
fn symbolic_variable_ext_degree_multiple_challenge() {
let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Challenge, 0);
assert_eq!(var.degree_multiple(), 0);
}
}