1use core::marker::PhantomData;
2
3use serde::{Deserialize, Serialize};
4
5#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
7pub enum BaseEntry {
8 Preprocessed { offset: usize },
9 Main { offset: usize },
10 Periodic,
11 Public,
12}
13
14#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
16pub enum ExtEntry {
17 Permutation { offset: usize },
18 Challenge,
19 PermutationValue,
20}
21
22#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
24pub struct SymbolicVariable<F> {
25 pub entry: BaseEntry,
26 pub index: usize,
27 #[serde(skip)]
28 pub(crate) _phantom: PhantomData<F>,
29}
30
31impl<F> SymbolicVariable<F> {
32 pub const fn new(entry: BaseEntry, index: usize) -> Self {
33 Self {
34 entry,
35 index,
36 _phantom: PhantomData,
37 }
38 }
39
40 pub const fn degree_multiple(&self) -> usize {
41 match self.entry {
42 BaseEntry::Preprocessed { .. } | BaseEntry::Main { .. } | BaseEntry::Periodic => 1,
45 BaseEntry::Public => 0,
46 }
47 }
48
49 pub fn poly_degree(&self, trace_len: usize, periodic_periods: &[usize]) -> usize {
57 match self.entry {
58 BaseEntry::Preprocessed { .. } | BaseEntry::Main { .. } => trace_len.saturating_sub(1),
59 BaseEntry::Periodic => {
60 let period = periodic_periods
64 .get(self.index)
65 .copied()
66 .unwrap_or(trace_len)
67 .min(trace_len);
68 match period {
69 0 => trace_len.saturating_sub(1),
70 p => trace_len - trace_len / p,
71 }
72 }
73 BaseEntry::Public => 0,
74 }
75 }
76}
77
78#[derive(Copy, Clone, Debug, Serialize, Deserialize)]
80pub struct SymbolicVariableExt<F, EF> {
81 pub entry: ExtEntry,
82 pub index: usize,
83 #[serde(skip)]
84 pub(crate) _phantom: PhantomData<(F, EF)>,
85}
86
87impl<F, EF> SymbolicVariableExt<F, EF> {
88 pub const fn new(entry: ExtEntry, index: usize) -> Self {
89 Self {
90 entry,
91 index,
92 _phantom: PhantomData,
93 }
94 }
95
96 pub const fn degree_multiple(&self) -> usize {
97 match self.entry {
98 ExtEntry::Permutation { .. } => 1,
99 ExtEntry::Challenge | ExtEntry::PermutationValue => 0,
100 }
101 }
102
103 pub const fn poly_degree(&self, trace_len: usize) -> usize {
106 match self.entry {
107 ExtEntry::Permutation { .. } => trace_len.saturating_sub(1),
108 ExtEntry::Challenge | ExtEntry::PermutationValue => 0,
109 }
110 }
111}
112
113#[cfg(test)]
114mod tests {
115 use p3_baby_bear::BabyBear;
116 use p3_field::extension::BinomialExtensionField;
117
118 use super::*;
119
120 type F = BabyBear;
121 type EF = BinomialExtensionField<F, 4>;
122
123 #[test]
124 fn symbolic_variable_new_main() {
125 let var = SymbolicVariable::<F>::new(BaseEntry::Main { offset: 1 }, 3);
127 assert_eq!(var.entry, BaseEntry::Main { offset: 1 });
128 assert_eq!(var.index, 3);
129 }
130
131 #[test]
132 fn symbolic_variable_new_preprocessed() {
133 let var = SymbolicVariable::<F>::new(BaseEntry::Preprocessed { offset: 0 }, 5);
135 assert_eq!(var.entry, BaseEntry::Preprocessed { offset: 0 });
136 assert_eq!(var.index, 5);
137 }
138
139 #[test]
140 fn symbolic_variable_new_public() {
141 let var = SymbolicVariable::<F>::new(BaseEntry::Public, 2);
143 assert_eq!(var.entry, BaseEntry::Public);
144 assert_eq!(var.index, 2);
145 }
146
147 #[test]
148 fn symbolic_variable_degree_multiple_main() {
149 let var = SymbolicVariable::<F>::new(BaseEntry::Main { offset: 0 }, 0);
151 assert_eq!(var.degree_multiple(), 1);
152 }
153
154 #[test]
155 fn symbolic_variable_degree_multiple_preprocessed() {
156 let var = SymbolicVariable::<F>::new(BaseEntry::Preprocessed { offset: 0 }, 0);
158 assert_eq!(var.degree_multiple(), 1);
159 }
160
161 #[test]
162 fn symbolic_variable_degree_multiple_public() {
163 let var = SymbolicVariable::<F>::new(BaseEntry::Public, 0);
165 assert_eq!(var.degree_multiple(), 0);
166 }
167
168 #[test]
169 fn symbolic_variable_ext_new_permutation() {
170 let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Permutation { offset: 1 }, 7);
172 assert_eq!(var.entry, ExtEntry::Permutation { offset: 1 });
173 assert_eq!(var.index, 7);
174 }
175
176 #[test]
177 fn symbolic_variable_ext_new_challenge() {
178 let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Challenge, 4);
180 assert_eq!(var.entry, ExtEntry::Challenge);
181 assert_eq!(var.index, 4);
182 }
183
184 #[test]
185 fn symbolic_variable_poly_degree_main_and_preprocessed() {
186 let main = SymbolicVariable::<F>::new(BaseEntry::Main { offset: 1 }, 0);
188 let prep = SymbolicVariable::<F>::new(BaseEntry::Preprocessed { offset: 0 }, 0);
189 assert_eq!(main.poly_degree(8, &[]), 7);
190 assert_eq!(prep.poly_degree(8, &[]), 7);
191 }
192
193 #[test]
194 fn symbolic_variable_poly_degree_public_is_constant() {
195 let var = SymbolicVariable::<F>::new(BaseEntry::Public, 0);
197 assert_eq!(var.poly_degree(8, &[]), 0);
198 }
199
200 #[test]
201 fn symbolic_variable_poly_degree_periodic() {
202 let var = SymbolicVariable::<F>::new(BaseEntry::Periodic, 0);
205 assert_eq!(var.poly_degree(8, &[2]), 4);
207 assert_eq!(var.poly_degree(8, &[4]), 6);
209 assert_eq!(var.poly_degree(8, &[8]), 7);
211 assert_eq!(var.poly_degree(8, &[1]), 0);
213 }
214
215 #[test]
216 fn symbolic_variable_poly_degree_periodic_missing_period_is_conservative() {
217 let var = SymbolicVariable::<F>::new(BaseEntry::Periodic, 3);
219 assert_eq!(var.poly_degree(8, &[]), 7);
220 }
221
222 #[test]
223 fn symbolic_variable_ext_poly_degree() {
224 let perm = SymbolicVariableExt::<F, EF>::new(ExtEntry::Permutation { offset: 0 }, 0);
226 let challenge = SymbolicVariableExt::<F, EF>::new(ExtEntry::Challenge, 0);
227 assert_eq!(perm.poly_degree(8), 7);
228 assert_eq!(challenge.poly_degree(8), 0);
229 }
230
231 #[test]
232 fn symbolic_variable_ext_degree_multiple_permutation() {
233 let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Permutation { offset: 0 }, 0);
235 assert_eq!(var.degree_multiple(), 1);
236 }
237
238 #[test]
239 fn symbolic_variable_ext_degree_multiple_challenge() {
240 let var = SymbolicVariableExt::<F, EF>::new(ExtEntry::Challenge, 0);
242 assert_eq!(var.degree_multiple(), 0);
243 }
244}