1use alloc::{sync::Arc, vec::Vec};
3
4use spin::RwLock;
5use spongefish::{Permutation, Unit};
6
7use crate::allocator::{FieldVar, VarAllocator};
8
9#[derive(Clone)]
12pub struct PermutationInstanceBuilder<T, const WIDTH: usize> {
13 allocator: VarAllocator<T>,
14 query_answers: Arc<RwLock<Vec<QueryAnswerPair<FieldVar, WIDTH>>>>,
15 linear_constraints: Arc<RwLock<LinearConstraints<FieldVar, T>>>,
16}
17
18#[derive(Clone, Debug, PartialEq, Eq)]
19pub struct LinearEquation<T, U> {
20 pub linear_combination: Vec<(U, T)>,
23 pub image: U,
25}
26
27impl<T, U> LinearEquation<T, U> {
28 #[must_use]
29 pub fn new(linear_combination: impl IntoIterator<Item = (U, T)>, image: U) -> Self {
30 Self {
31 linear_combination: linear_combination.into_iter().collect(),
32 image,
33 }
34 }
35}
36
37impl<T, U: Unit> Default for LinearEquation<T, U> {
38 fn default() -> Self {
39 Self {
40 linear_combination: Vec::new(),
41 image: U::ZERO,
42 }
43 }
44}
45
46#[derive(Clone, Debug, PartialEq, Eq)]
47pub struct LinearConstraints<T, U> {
48 pub equations: Vec<LinearEquation<T, U>>,
49}
50
51impl<T, U> AsRef<[LinearEquation<T, U>]> for LinearConstraints<T, U> {
52 fn as_ref(&self) -> &[LinearEquation<T, U>] {
53 &self.equations
54 }
55}
56
57impl<T, U> Default for LinearConstraints<T, U> {
58 fn default() -> Self {
59 Self {
60 equations: Vec::new(),
61 }
62 }
63}
64
65#[derive(Clone, Debug, PartialEq, Eq)]
66pub struct QueryAnswerPair<T, const WIDTH: usize> {
67 pub input: [T; WIDTH],
68 pub output: [T; WIDTH],
69}
70
71impl<T, const WIDTH: usize> QueryAnswerPair<T, WIDTH> {
72 #[must_use]
73 pub const fn new(input: [T; WIDTH], output: [T; WIDTH]) -> Self {
74 Self { input, output }
75 }
76}
77
78#[derive(Clone)]
79pub struct PermutationWitnessBuilder<P: Permutation<WIDTH>, const WIDTH: usize> {
80 permutation: P,
81 trace: Arc<RwLock<Vec<QueryAnswerPair<P::U, WIDTH>>>>,
82 linear_constraints: Arc<RwLock<LinearConstraints<P::U, P::U>>>,
83}
84
85#[derive(Clone, Debug, PartialEq, Eq)]
87pub struct PermutationInstance<T, const WIDTH: usize> {
88 pub vars_count: usize,
89 pub public_values: Vec<(FieldVar, T)>,
90 pub query_answers: Vec<QueryAnswerPair<FieldVar, WIDTH>>,
92 pub linear_constraints: LinearConstraints<FieldVar, T>,
93}
94
95impl<T, const WIDTH: usize> PermutationInstance<T, WIDTH> {
96 #[must_use]
97 pub fn constraints(&self) -> impl AsRef<[QueryAnswerPair<FieldVar, WIDTH>]> + '_ {
98 &self.query_answers
99 }
100
101 #[must_use]
102 pub const fn linear_constraints(&self) -> &LinearConstraints<FieldVar, T> {
103 &self.linear_constraints
104 }
105
106 #[must_use]
107 pub fn public_vars(&self) -> &[(FieldVar, T)] {
108 &self.public_values
109 }
110}
111
112#[derive(Clone, Debug, PartialEq, Eq)]
114pub struct PermutationWitness<T, const WIDTH: usize> {
115 pub trace: Vec<QueryAnswerPair<T, WIDTH>>,
116 pub linear_constraints: LinearConstraints<T, T>,
117}
118
119impl<T, const WIDTH: usize> PermutationWitness<T, WIDTH> {
120 #[must_use]
121 pub fn trace(&self) -> impl AsRef<[QueryAnswerPair<T, WIDTH>]> + '_ {
122 &self.trace
123 }
124
125 #[must_use]
126 pub const fn linear_constraints(&self) -> &LinearConstraints<T, T> {
127 &self.linear_constraints
128 }
129}
130
131impl<T: Unit, const WIDTH: usize> Permutation<WIDTH> for PermutationInstanceBuilder<T, WIDTH> {
132 type U = FieldVar;
133
134 fn permute(&self, state: &[Self::U; WIDTH]) -> [Self::U; WIDTH] {
135 self.allocate_permutation(state)
136 }
137}
138
139impl<P: Permutation<WIDTH>, const WIDTH: usize> Permutation<WIDTH>
140 for PermutationWitnessBuilder<P, WIDTH>
141{
142 type U = P::U;
143
144 fn permute(&self, state: &[Self::U; WIDTH]) -> [Self::U; WIDTH] {
145 self.allocate_permutation(state)
146 }
147}
148
149impl<T: Clone + Unit, const WIDTH: usize> Default for PermutationInstanceBuilder<T, WIDTH> {
150 fn default() -> Self {
151 Self::new()
152 }
153}
154
155impl<T: Clone + Unit, const WIDTH: usize> PermutationInstanceBuilder<T, WIDTH> {
156 #[must_use]
157 pub fn with_allocator(allocator: VarAllocator<T>) -> Self {
158 Self {
159 allocator,
160 query_answers: Default::default(),
161 linear_constraints: Default::default(),
162 }
163 }
164
165 #[must_use]
166 pub fn new() -> Self {
167 Self::with_allocator(VarAllocator::new())
168 }
169
170 #[must_use]
171 pub const fn allocator(&self) -> &VarAllocator<T> {
172 &self.allocator
173 }
174
175 #[must_use]
176 pub fn allocate_permutation(&self, &input: &[FieldVar; WIDTH]) -> [FieldVar; WIDTH] {
177 let output = self.allocator.allocate_vars();
178 self.add_permutation(input, output);
179 output
180 }
181
182 pub fn add_permutation(&self, input: [FieldVar; WIDTH], output: [FieldVar; WIDTH]) {
183 debug_assert!(input
184 .iter()
185 .chain(output.iter())
186 .all(|var| self.allocator.is_allocated(*var)));
187 self.query_answers
188 .write()
189 .push(QueryAnswerPair::new(input, output));
190 }
191
192 pub fn add_equation(&self, equation: LinearEquation<FieldVar, T>)
193 where
194 T: PartialEq,
195 {
196 let constraints = self.query_answers.read();
197 for (_, var) in &equation.linear_combination {
198 assert!(
199 self.allocator.is_allocated(*var),
200 "unallocated variable {}",
201 var.index(),
202 );
203 }
204 for (term_idx, (coeff, var)) in equation.linear_combination.iter().enumerate() {
205 if *coeff == T::ZERO {
206 continue;
207 }
208 assert!(
209 constraints
210 .iter()
211 .flat_map(|pair| pair.input.iter().chain(pair.output.iter()))
212 .any(|known_var| known_var == var),
213 "linear equation term {term_idx} references variable {}, \
214 but nonzero linear terms must reference a permutation input or output variable",
215 var.index(),
216 );
217 }
218 self.linear_constraints.write().equations.push(equation);
219 }
220
221 #[must_use]
222 pub fn constraints(&self) -> impl AsRef<[QueryAnswerPair<FieldVar, WIDTH>]> {
223 self.query_answers.read().clone()
224 }
225
226 #[must_use]
227 pub fn linear_constraints(&self) -> LinearConstraints<FieldVar, T> {
228 self.linear_constraints.read().clone()
229 }
230
231 #[must_use]
232 pub fn public_vars(&self) -> Vec<(FieldVar, T)> {
233 self.allocator.public_vars()
234 }
235
236 #[must_use]
237 pub fn snapshot(&self) -> PermutationInstance<T, WIDTH> {
238 PermutationInstance {
239 vars_count: self.allocator.vars_count(),
240 public_values: self.allocator.public_vars(),
241 query_answers: self.constraints().as_ref().to_vec(),
242 linear_constraints: self.linear_constraints(),
243 }
244 }
245}
246
247impl<P: Permutation<WIDTH>, const WIDTH: usize> From<P> for PermutationWitnessBuilder<P, WIDTH> {
248 fn from(value: P) -> Self {
249 Self::new(value)
250 }
251}
252
253impl<P: Permutation<WIDTH>, const WIDTH: usize> PermutationWitnessBuilder<P, WIDTH> {
254 #[must_use]
255 pub fn new(permutation: P) -> Self {
256 Self {
257 permutation,
258 trace: Default::default(),
259 linear_constraints: Default::default(),
260 }
261 }
262
263 #[must_use]
264 pub fn allocate_permutation(&self, input: &[P::U; WIDTH]) -> [P::U; WIDTH] {
265 let output = self.permutation.permute(input);
266 self.add_permutation(input, &output);
267 output
268 }
269
270 pub fn add_permutation(&self, input: &[P::U; WIDTH], output: &[P::U; WIDTH]) {
271 self.trace
272 .write()
273 .push(QueryAnswerPair::new(input.clone(), output.clone()));
274 }
275
276 pub fn add_equation(&self, equation: LinearEquation<P::U, P::U>) {
277 self.linear_constraints.write().equations.push(equation);
278 }
279
280 #[must_use]
281 pub fn trace(&self) -> impl AsRef<[QueryAnswerPair<P::U, WIDTH>]> {
282 self.trace.read().clone()
283 }
284
285 #[must_use]
286 pub fn linear_constraints(&self) -> LinearConstraints<P::U, P::U> {
287 self.linear_constraints.read().clone()
288 }
289
290 #[must_use]
291 pub fn snapshot(&self) -> PermutationWitness<P::U, WIDTH> {
292 PermutationWitness {
293 trace: self.trace().as_ref().to_vec(),
294 linear_constraints: self.linear_constraints(),
295 }
296 }
297}