1use crate::{
2 circuits::{
3 berkeley_columns,
4 berkeley_columns::BerkeleyChallengeTerm,
5 constraints::FeatureFlags,
6 domains::Domain,
7 gate::CurrOrNext,
8 lookup::lookups::{LookupPattern, LookupPatterns},
9 polynomials::{
10 foreign_field_common::KimchiForeignElement, permutation::eval_vanishes_on_last_n_rows,
11 },
12 },
13 collections::{HashMap, HashSet},
14 proof::PointEvaluations,
15};
16use alloc::{
17 boxed::Box,
18 format,
19 string::{String, ToString},
20 vec,
21 vec::Vec,
22};
23use ark_ff::{FftField, Field, One, PrimeField, Zero};
24use ark_poly::{
25 univariate::DensePolynomial, EvaluationDomain, Evaluations, Radix2EvaluationDomain as D,
26};
27use core::{
28 cmp::Ordering,
29 fmt,
30 fmt::{Debug, Display},
31 iter::FromIterator,
32 ops::{Add, AddAssign, Index, Mul, MulAssign, Neg, Sub},
33};
34use itertools::Itertools;
35use o1_utils::{field_helpers::pows, foreign_field::ForeignFieldHelpers, FieldHelpers};
36#[cfg(feature = "parallel")]
37use rayon::prelude::*;
38use serde::{Deserialize, Serialize};
39use thiserror::Error;
40use CurrOrNext::{Curr, Next};
41
42use self::constraints::ExprOps;
43
44#[derive(Debug, Error)]
45pub enum ExprError<Column> {
46 #[error("Empty stack")]
47 EmptyStack,
48
49 #[error("Lookup should not have been used")]
50 LookupShouldNotBeUsed,
51
52 #[error("Linearization failed (needed {0:?} evaluated at the {1:?} row")]
53 MissingEvaluation(Column, CurrOrNext),
54
55 #[error("Cannot get index evaluation {0:?} (should have been linearized away)")]
56 MissingIndexEvaluation(Column),
57
58 #[error("Linearization failed (too many unevaluated columns: {0:?}")]
59 FailedLinearization(Vec<Variable<Column>>),
60
61 #[error("runtime table not available")]
62 MissingRuntime,
63}
64
65pub trait AlphaChallengeTerm<'a>:
68 Copy + Clone + Debug + PartialEq + Eq + Serialize + Deserialize<'a> + Display
69{
70 const ALPHA: Self;
71}
72
73#[derive(Clone)]
75pub struct Constants<F: 'static> {
76 pub endo_coefficient: F,
78 pub mds: &'static [[F; 3]; 3],
80 pub zk_rows: u64,
82}
83
84pub trait ColumnEnvironment<
85 'a,
86 F: FftField,
87 ChallengeTerm,
88 Challenges: Index<ChallengeTerm, Output = F>,
89>
90{
91 type Column;
97
98 fn get_column(&self, col: &Self::Column) -> Option<&'a Evaluations<F, D<F>>>;
100
101 fn column_domain(&self, col: &Self::Column) -> Domain;
103
104 fn get_domain(&self, d: Domain) -> D<F>;
105
106 fn get_constants(&self) -> &Constants<F>;
110
111 fn get_challenges(&self) -> &Challenges;
113
114 fn vanishes_on_zero_knowledge_and_previous_rows(&self) -> &'a Evaluations<F, D<F>>;
115
116 fn l0_1(&self) -> F;
119}
120
121pub fn l0_1<F: FftField>(d: D<F>) -> F {
134 d.elements()
135 .skip(1)
136 .fold(F::one(), |acc, omega_j| acc * (F::one() - omega_j))
137}
138
139pub fn unnormalized_lagrange_basis<F: FftField>(domain: &D<F>, i: i32, pt: &F) -> F {
141 let omega_i = if i < 0 {
142 domain.group_gen.pow([-i as u64]).inverse().unwrap()
143 } else {
144 domain.group_gen.pow([i as u64])
145 };
146 domain.evaluate_vanishing_polynomial(*pt) / (*pt - omega_i)
147}
148
149#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
150pub struct Variable<Column> {
153 pub col: Column,
155 pub row: CurrOrNext,
157}
158
159#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
169pub enum ConstantTerm<F> {
170 EndoCoefficient,
171 Mds { row: usize, col: usize },
172 Literal(F),
173}
174
175pub trait Literal: Sized + Clone {
176 type F;
177
178 fn literal(x: Self::F) -> Self;
179
180 fn to_literal(self) -> Result<Self::F, Self>;
181
182 fn to_literal_ref(&self) -> Option<&Self::F>;
183
184 fn as_literal(&self, constants: &Constants<Self::F>) -> Self;
188}
189
190impl<F: Field> Literal for F {
191 type F = F;
192
193 fn literal(x: Self::F) -> Self {
194 x
195 }
196
197 fn to_literal(self) -> Result<Self::F, Self> {
198 Ok(self)
199 }
200
201 fn to_literal_ref(&self) -> Option<&Self::F> {
202 Some(self)
203 }
204
205 fn as_literal(&self, _constants: &Constants<Self::F>) -> Self {
206 *self
207 }
208}
209
210impl<F: Clone> Literal for ConstantTerm<F> {
211 type F = F;
212 fn literal(x: Self::F) -> Self {
213 ConstantTerm::Literal(x)
214 }
215 fn to_literal(self) -> Result<Self::F, Self> {
216 match self {
217 ConstantTerm::Literal(x) => Ok(x),
218 x => Err(x),
219 }
220 }
221 fn to_literal_ref(&self) -> Option<&Self::F> {
222 match self {
223 ConstantTerm::Literal(x) => Some(x),
224 _ => None,
225 }
226 }
227 fn as_literal(&self, constants: &Constants<Self::F>) -> Self {
228 match self {
229 ConstantTerm::EndoCoefficient => {
230 ConstantTerm::Literal(constants.endo_coefficient.clone())
231 }
232 ConstantTerm::Mds { row, col } => {
233 ConstantTerm::Literal(constants.mds[*row][*col].clone())
234 }
235 ConstantTerm::Literal(_) => self.clone(),
236 }
237 }
238}
239
240#[derive(Clone, Debug, PartialEq)]
241pub enum ConstantExprInner<F, ChallengeTerm> {
242 Challenge(ChallengeTerm),
243 Constant(ConstantTerm<F>),
244}
245
246impl<'a, F: Clone, ChallengeTerm: AlphaChallengeTerm<'a>> Literal
247 for ConstantExprInner<F, ChallengeTerm>
248{
249 type F = F;
250 fn literal(x: Self::F) -> Self {
251 Self::Constant(ConstantTerm::literal(x))
252 }
253 fn to_literal(self) -> Result<Self::F, Self> {
254 match self {
255 Self::Constant(x) => match x.to_literal() {
256 Ok(x) => Ok(x),
257 Err(x) => Err(Self::Constant(x)),
258 },
259 x => Err(x),
260 }
261 }
262 fn to_literal_ref(&self) -> Option<&Self::F> {
263 match self {
264 Self::Constant(x) => x.to_literal_ref(),
265 _ => None,
266 }
267 }
268 fn as_literal(&self, constants: &Constants<Self::F>) -> Self {
269 match self {
270 Self::Constant(x) => Self::Constant(x.as_literal(constants)),
271 Self::Challenge(_) => self.clone(),
272 }
273 }
274}
275
276impl<'a, F, ChallengeTerm: AlphaChallengeTerm<'a>> From<ChallengeTerm>
277 for ConstantExprInner<F, ChallengeTerm>
278{
279 fn from(x: ChallengeTerm) -> Self {
280 ConstantExprInner::Challenge(x)
281 }
282}
283
284impl<F, ChallengeTerm> From<ConstantTerm<F>> for ConstantExprInner<F, ChallengeTerm> {
285 fn from(x: ConstantTerm<F>) -> Self {
286 ConstantExprInner::Constant(x)
287 }
288}
289
290#[derive(Clone, Debug, PartialEq, Eq, Hash)]
291pub enum Operations<T> {
292 Atom(T),
293 Pow(Box<Self>, u64),
294 Add(Box<Self>, Box<Self>),
295 Mul(Box<Self>, Box<Self>),
296 Sub(Box<Self>, Box<Self>),
297 Double(Box<Self>),
298 Square(Box<Self>),
299 Cache(CacheId, Box<Self>),
300 IfFeature(FeatureFlag, Box<Self>, Box<Self>),
301}
302
303impl<T> From<T> for Operations<T> {
304 fn from(x: T) -> Self {
305 Operations::Atom(x)
306 }
307}
308
309impl<T: Literal + Clone> Literal for Operations<T> {
310 type F = T::F;
311
312 fn literal(x: Self::F) -> Self {
313 Self::Atom(T::literal(x))
314 }
315
316 fn to_literal(self) -> Result<Self::F, Self> {
317 match self {
318 Self::Atom(x) => match x.to_literal() {
319 Ok(x) => Ok(x),
320 Err(x) => Err(Self::Atom(x)),
321 },
322 x => Err(x),
323 }
324 }
325
326 fn to_literal_ref(&self) -> Option<&Self::F> {
327 match self {
328 Self::Atom(x) => x.to_literal_ref(),
329 _ => None,
330 }
331 }
332
333 fn as_literal(&self, constants: &Constants<Self::F>) -> Self {
334 match self {
335 Self::Atom(x) => Self::Atom(x.as_literal(constants)),
336 Self::Pow(x, n) => Self::Pow(Box::new(x.as_literal(constants)), *n),
337 Self::Add(x, y) => Self::Add(
338 Box::new(x.as_literal(constants)),
339 Box::new(y.as_literal(constants)),
340 ),
341 Self::Mul(x, y) => Self::Mul(
342 Box::new(x.as_literal(constants)),
343 Box::new(y.as_literal(constants)),
344 ),
345 Self::Sub(x, y) => Self::Sub(
346 Box::new(x.as_literal(constants)),
347 Box::new(y.as_literal(constants)),
348 ),
349 Self::Double(x) => Self::Double(Box::new(x.as_literal(constants))),
350 Self::Square(x) => Self::Square(Box::new(x.as_literal(constants))),
351 Self::Cache(id, x) => Self::Cache(*id, Box::new(x.as_literal(constants))),
352 Self::IfFeature(flag, if_true, if_false) => Self::IfFeature(
353 *flag,
354 Box::new(if_true.as_literal(constants)),
355 Box::new(if_false.as_literal(constants)),
356 ),
357 }
358 }
359}
360
361pub type ConstantExpr<F, ChallengeTerm> = Operations<ConstantExprInner<F, ChallengeTerm>>;
362
363impl<F, ChallengeTerm> From<ConstantTerm<F>> for ConstantExpr<F, ChallengeTerm> {
364 fn from(x: ConstantTerm<F>) -> Self {
365 ConstantExprInner::from(x).into()
366 }
367}
368
369impl<'a, F, ChallengeTerm: AlphaChallengeTerm<'a>> From<ChallengeTerm>
370 for ConstantExpr<F, ChallengeTerm>
371{
372 fn from(x: ChallengeTerm) -> Self {
373 ConstantExprInner::from(x).into()
374 }
375}
376
377impl<F: Copy, ChallengeTerm: Copy> ConstantExprInner<F, ChallengeTerm> {
378 fn to_polish<Column>(
379 &self,
380 _cache: &mut HashMap<CacheId, usize>,
381 res: &mut Vec<PolishToken<F, Column, ChallengeTerm>>,
382 ) {
383 match self {
384 ConstantExprInner::Challenge(chal) => res.push(PolishToken::Challenge(*chal)),
385 ConstantExprInner::Constant(c) => res.push(PolishToken::Constant(*c)),
386 }
387 }
388}
389
390impl<F: Copy, ChallengeTerm: Copy> Operations<ConstantExprInner<F, ChallengeTerm>> {
391 fn to_polish<Column>(
392 &self,
393 cache: &mut HashMap<CacheId, usize>,
394 res: &mut Vec<PolishToken<F, Column, ChallengeTerm>>,
395 ) {
396 match self {
397 Operations::Atom(atom) => atom.to_polish(cache, res),
398 Operations::Add(x, y) => {
399 x.as_ref().to_polish(cache, res);
400 y.as_ref().to_polish(cache, res);
401 res.push(PolishToken::Add)
402 }
403 Operations::Mul(x, y) => {
404 x.as_ref().to_polish(cache, res);
405 y.as_ref().to_polish(cache, res);
406 res.push(PolishToken::Mul)
407 }
408 Operations::Sub(x, y) => {
409 x.as_ref().to_polish(cache, res);
410 y.as_ref().to_polish(cache, res);
411 res.push(PolishToken::Sub)
412 }
413 Operations::Pow(x, n) => {
414 x.to_polish(cache, res);
415 res.push(PolishToken::Pow(*n))
416 }
417 Operations::Double(x) => {
418 x.to_polish(cache, res);
419 res.push(PolishToken::Dup);
420 res.push(PolishToken::Add);
421 }
422 Operations::Square(x) => {
423 x.to_polish(cache, res);
424 res.push(PolishToken::Dup);
425 res.push(PolishToken::Mul);
426 }
427 Operations::Cache(id, x) => {
428 match cache.get(id) {
429 Some(pos) =>
430 {
432 res.push(PolishToken::Load(*pos))
433 }
434 None => {
435 x.to_polish(cache, res);
437 res.push(PolishToken::Store);
438 cache.insert(*id, cache.len());
439 }
440 }
441 }
442 Operations::IfFeature(feature, if_true, if_false) => {
443 {
444 let tok = PolishToken::SkipIfNot(*feature, 0);
446 res.push(tok);
447 let len_before = res.len();
448 let mut cache = cache.clone();
451 if_true.to_polish(&mut cache, res);
452 let len_after = res.len();
453 res[len_before - 1] = PolishToken::SkipIfNot(*feature, len_after - len_before);
454 }
455
456 {
457 let tok = PolishToken::SkipIfNot(*feature, 0);
459 res.push(tok);
460 let len_before = res.len();
461 let mut cache = cache.clone();
464 if_false.to_polish(&mut cache, res);
465 let len_after = res.len();
466 res[len_before - 1] = PolishToken::SkipIfNot(*feature, len_after - len_before);
467 }
468 }
469 }
470 }
471}
472
473impl<T: Literal> Operations<T>
474where
475 T::F: Field,
476{
477 pub fn pow(self, p: u64) -> Self {
479 if p == 0 {
480 return Self::literal(T::F::one());
481 }
482 match self.to_literal() {
483 Ok(l) => Self::literal(<T::F as Field>::pow(&l, [p])),
484 Err(x) => Self::Pow(Box::new(x), p),
485 }
486 }
487}
488
489impl<F: Field, ChallengeTerm: Copy> ConstantExpr<F, ChallengeTerm> {
490 pub fn value(&self, c: &Constants<F>, chals: &dyn Index<ChallengeTerm, Output = F>) -> F {
492 use ConstantExprInner::*;
493 use Operations::*;
494 match self {
495 Atom(Challenge(challenge_term)) => chals[*challenge_term],
496 Atom(Constant(ConstantTerm::EndoCoefficient)) => c.endo_coefficient,
497 Atom(Constant(ConstantTerm::Mds { row, col })) => c.mds[*row][*col],
498 Atom(Constant(ConstantTerm::Literal(x))) => *x,
499 Pow(x, p) => x.value(c, chals).pow([*p]),
500 Mul(x, y) => x.value(c, chals) * y.value(c, chals),
501 Add(x, y) => x.value(c, chals) + y.value(c, chals),
502 Sub(x, y) => x.value(c, chals) - y.value(c, chals),
503 Double(x) => x.value(c, chals).double(),
504 Square(x) => x.value(c, chals).square(),
505 Cache(_, x) => {
506 x.value(c, chals)
508 }
509 IfFeature(_flag, _if_true, _if_false) => todo!(),
510 }
511 }
512}
513
514#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
516pub struct CacheId(usize);
517
518#[derive(Default)]
520pub struct Cache {
521 next_id: usize,
522}
523
524impl CacheId {
525 fn get_from<'b, F: FftField>(
526 &self,
527 cache: &'b HashMap<CacheId, EvalResult<'_, F>>,
528 ) -> Option<EvalResult<'b, F>> {
529 cache.get(self).map(|e| match e {
530 EvalResult::Constant(x) => EvalResult::Constant(*x),
531 EvalResult::SubEvals {
532 domain,
533 shift,
534 evals,
535 } => EvalResult::SubEvals {
536 domain: *domain,
537 shift: *shift,
538 evals,
539 },
540 EvalResult::Evals { domain, evals } => EvalResult::SubEvals {
541 domain: *domain,
542 shift: 0,
543 evals,
544 },
545 })
546 }
547
548 fn var_name(&self) -> String {
549 format!("x_{}", self.0)
550 }
551
552 fn latex_name(&self) -> String {
553 format!("x_{{{}}}", self.0)
554 }
555}
556
557impl Cache {
558 fn next_id(&mut self) -> CacheId {
559 let id = self.next_id;
560 self.next_id += 1;
561 CacheId(id)
562 }
563
564 pub fn cache<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(&mut self, e: T) -> T {
565 e.cache(self)
566 }
567}
568
569#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize, Hash)]
571#[cfg_attr(
572 feature = "ocaml_types",
573 derive(ocaml::IntoValue, ocaml::FromValue, ocaml_gen::Enum)
574)]
575pub enum FeatureFlag {
576 RangeCheck0,
577 RangeCheck1,
578 ForeignFieldAdd,
579 ForeignFieldMul,
580 Xor,
581 Rot,
582 LookupTables,
583 RuntimeLookupTables,
584 LookupPattern(LookupPattern),
585 TableWidth(isize), LookupsPerRow(isize), }
590
591impl FeatureFlag {
592 fn is_enabled(&self) -> bool {
593 todo!("Handle features")
594 }
595}
596
597#[derive(Copy, Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
598pub struct RowOffset {
599 pub zk_rows: bool,
600 pub offset: i32,
601}
602
603#[derive(Clone, Debug, PartialEq)]
604pub enum ExprInner<C, Column> {
605 Constant(C),
606 Cell(Variable<Column>),
607 VanishesOnZeroKnowledgeAndPreviousRows,
608 UnnormalizedLagrangeBasis(RowOffset),
611}
612
613pub type Expr<C, Column> = Operations<ExprInner<C, Column>>;
624
625impl<F, Column, ChallengeTerm> From<ConstantExpr<F, ChallengeTerm>>
626 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
627{
628 fn from(x: ConstantExpr<F, ChallengeTerm>) -> Self {
629 Expr::Atom(ExprInner::Constant(x))
630 }
631}
632
633impl<'a, F, Column, ChallengeTerm: AlphaChallengeTerm<'a>> From<ConstantTerm<F>>
634 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
635{
636 fn from(x: ConstantTerm<F>) -> Self {
637 ConstantExpr::from(x).into()
638 }
639}
640
641impl<'a, F, Column, ChallengeTerm: AlphaChallengeTerm<'a>> From<ChallengeTerm>
642 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
643{
644 fn from(x: ChallengeTerm) -> Self {
645 ConstantExpr::from(x).into()
646 }
647}
648
649impl<T: Literal, Column: Clone> Literal for ExprInner<T, Column> {
650 type F = T::F;
651
652 fn literal(x: Self::F) -> Self {
653 ExprInner::Constant(T::literal(x))
654 }
655
656 fn to_literal(self) -> Result<Self::F, Self> {
657 match self {
658 ExprInner::Constant(x) => match x.to_literal() {
659 Ok(x) => Ok(x),
660 Err(x) => Err(ExprInner::Constant(x)),
661 },
662 x => Err(x),
663 }
664 }
665
666 fn to_literal_ref(&self) -> Option<&Self::F> {
667 match self {
668 ExprInner::Constant(x) => x.to_literal_ref(),
669 _ => None,
670 }
671 }
672
673 fn as_literal(&self, constants: &Constants<Self::F>) -> Self {
674 match self {
675 ExprInner::Constant(x) => ExprInner::Constant(x.as_literal(constants)),
676 ExprInner::Cell(_)
677 | ExprInner::VanishesOnZeroKnowledgeAndPreviousRows
678 | ExprInner::UnnormalizedLagrangeBasis(_) => self.clone(),
679 }
680 }
681}
682
683impl<T: Literal + PartialEq> Operations<T>
684where
685 T::F: Field,
686{
687 fn apply_feature_flags_inner(&self, features: &FeatureFlags) -> (Self, bool) {
688 use Operations::*;
689 match self {
690 Atom(_) => (self.clone(), false),
691 Double(c) => {
692 let (c_reduced, reduce_further) = c.apply_feature_flags_inner(features);
693 if reduce_further && c_reduced.is_zero() {
694 (Self::zero(), true)
695 } else {
696 (Double(Box::new(c_reduced)), false)
697 }
698 }
699 Square(c) => {
700 let (c_reduced, reduce_further) = c.apply_feature_flags_inner(features);
701 if reduce_further && (c_reduced.is_zero() || c_reduced.is_one()) {
702 (c_reduced, true)
703 } else {
704 (Square(Box::new(c_reduced)), false)
705 }
706 }
707 Add(c1, c2) => {
708 let (c1_reduced, reduce_further1) = c1.apply_feature_flags_inner(features);
709 let (c2_reduced, reduce_further2) = c2.apply_feature_flags_inner(features);
710 if reduce_further1 && c1_reduced.is_zero() {
711 if reduce_further2 && c2_reduced.is_zero() {
712 (Self::zero(), true)
713 } else {
714 (c2_reduced, false)
715 }
716 } else if reduce_further2 && c2_reduced.is_zero() {
717 (c1_reduced, false)
718 } else {
719 (Add(Box::new(c1_reduced), Box::new(c2_reduced)), false)
720 }
721 }
722 Sub(c1, c2) => {
723 let (c1_reduced, reduce_further1) = c1.apply_feature_flags_inner(features);
724 let (c2_reduced, reduce_further2) = c2.apply_feature_flags_inner(features);
725 if reduce_further1 && c1_reduced.is_zero() {
726 if reduce_further2 && c2_reduced.is_zero() {
727 (Self::zero(), true)
728 } else {
729 (-c2_reduced, false)
730 }
731 } else if reduce_further2 && c2_reduced.is_zero() {
732 (c1_reduced, false)
733 } else {
734 (Sub(Box::new(c1_reduced), Box::new(c2_reduced)), false)
735 }
736 }
737 Mul(c1, c2) => {
738 let (c1_reduced, reduce_further1) = c1.apply_feature_flags_inner(features);
739 let (c2_reduced, reduce_further2) = c2.apply_feature_flags_inner(features);
740 if reduce_further1 && c1_reduced.is_zero()
741 || reduce_further2 && c2_reduced.is_zero()
742 {
743 (Self::zero(), true)
744 } else if reduce_further1 && c1_reduced.is_one() {
745 if reduce_further2 && c2_reduced.is_one() {
746 (Self::one(), true)
747 } else {
748 (c2_reduced, false)
749 }
750 } else if reduce_further2 && c2_reduced.is_one() {
751 (c1_reduced, false)
752 } else {
753 (Mul(Box::new(c1_reduced), Box::new(c2_reduced)), false)
754 }
755 }
756 Pow(c, power) => {
757 let (c_reduced, reduce_further) = c.apply_feature_flags_inner(features);
758 if reduce_further && (c_reduced.is_zero() || c_reduced.is_one()) {
759 (c_reduced, true)
760 } else {
761 (Pow(Box::new(c_reduced), *power), false)
762 }
763 }
764 Cache(cache_id, c) => {
765 let (c_reduced, reduce_further) = c.apply_feature_flags_inner(features);
766 if reduce_further {
767 (c_reduced, true)
768 } else {
769 (Cache(*cache_id, Box::new(c_reduced)), false)
770 }
771 }
772 IfFeature(feature, c1, c2) => {
773 let is_enabled = {
774 use FeatureFlag::*;
775 match feature {
776 RangeCheck0 => features.range_check0,
777 RangeCheck1 => features.range_check1,
778 ForeignFieldAdd => features.foreign_field_add,
779 ForeignFieldMul => features.foreign_field_mul,
780 Xor => features.xor,
781 Rot => features.rot,
782 LookupTables => {
783 features.lookup_features.patterns != LookupPatterns::default()
784 }
785 RuntimeLookupTables => features.lookup_features.uses_runtime_tables,
786 LookupPattern(pattern) => features.lookup_features.patterns[*pattern],
787 TableWidth(width) => features
788 .lookup_features
789 .patterns
790 .into_iter()
791 .any(|feature| feature.max_joint_size() >= (*width as u32)),
792 LookupsPerRow(count) => features
793 .lookup_features
794 .patterns
795 .into_iter()
796 .any(|feature| feature.max_lookups_per_row() >= (*count as usize)),
797 }
798 };
799 if is_enabled {
800 let (c1_reduced, _) = c1.apply_feature_flags_inner(features);
801 (c1_reduced, false)
802 } else {
803 let (c2_reduced, _) = c2.apply_feature_flags_inner(features);
804 (c2_reduced, true)
805 }
806 }
807 }
808 }
809 pub fn apply_feature_flags(&self, features: &FeatureFlags) -> Self {
810 let (res, _) = self.apply_feature_flags_inner(features);
811 res
812 }
813}
814
815#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
819pub enum PolishToken<F, Column, ChallengeTerm> {
820 Constant(ConstantTerm<F>),
821 Challenge(ChallengeTerm),
822 Cell(Variable<Column>),
823 Dup,
824 Pow(u64),
825 Add,
826 Mul,
827 Sub,
828 VanishesOnZeroKnowledgeAndPreviousRows,
829 UnnormalizedLagrangeBasis(RowOffset),
830 Store,
831 Load(usize),
832 SkipIf(FeatureFlag, usize),
834 SkipIfNot(FeatureFlag, usize),
836}
837
838pub trait ColumnEvaluations<F> {
839 type Column;
840 fn evaluate(&self, col: Self::Column) -> Result<PointEvaluations<F>, ExprError<Self::Column>>;
841}
842
843impl<Column: Copy> Variable<Column> {
844 fn evaluate<F: Field, Evaluations: ColumnEvaluations<F, Column = Column>>(
845 &self,
846 evals: &Evaluations,
847 ) -> Result<F, ExprError<Column>> {
848 let point_evaluations = evals.evaluate(self.col)?;
849 match self.row {
850 CurrOrNext::Curr => Ok(point_evaluations.zeta),
851 CurrOrNext::Next => Ok(point_evaluations.zeta_omega),
852 }
853 }
854}
855
856impl<F: FftField, Column: Copy, ChallengeTerm: Copy> PolishToken<F, Column, ChallengeTerm> {
857 pub fn evaluate<Evaluations: ColumnEvaluations<F, Column = Column>>(
859 toks: &[PolishToken<F, Column, ChallengeTerm>],
860 d: D<F>,
861 pt: F,
862 evals: &Evaluations,
863 c: &Constants<F>,
864 chals: &dyn Index<ChallengeTerm, Output = F>,
865 ) -> Result<F, ExprError<Column>> {
866 let mut stack = vec![];
867 let mut cache: Vec<F> = vec![];
868
869 let mut skip_count = 0;
870
871 for t in toks.iter() {
872 if skip_count > 0 {
873 skip_count -= 1;
874 continue;
875 }
876
877 use ConstantTerm::*;
878 use PolishToken::*;
879 match t {
880 Challenge(challenge_term) => stack.push(chals[*challenge_term]),
881 Constant(EndoCoefficient) => stack.push(c.endo_coefficient),
882 Constant(Mds { row, col }) => stack.push(c.mds[*row][*col]),
883 VanishesOnZeroKnowledgeAndPreviousRows => {
884 stack.push(eval_vanishes_on_last_n_rows(d, c.zk_rows + 1, pt))
885 }
886 UnnormalizedLagrangeBasis(i) => {
887 let offset = if i.zk_rows {
888 -(c.zk_rows as i32) + i.offset
889 } else {
890 i.offset
891 };
892 stack.push(unnormalized_lagrange_basis(&d, offset, &pt))
893 }
894 Constant(Literal(x)) => stack.push(*x),
895 Dup => stack.push(stack[stack.len() - 1]),
896 Cell(v) => stack.push(v.evaluate(evals)?),
897 Pow(n) => {
898 let i = stack.len() - 1;
899 stack[i] = stack[i].pow([*n]);
900 }
901 Add => {
902 let y = stack.pop().ok_or(ExprError::EmptyStack)?;
903 let x = stack.pop().ok_or(ExprError::EmptyStack)?;
904 stack.push(x + y);
905 }
906 Mul => {
907 let y = stack.pop().ok_or(ExprError::EmptyStack)?;
908 let x = stack.pop().ok_or(ExprError::EmptyStack)?;
909 stack.push(x * y);
910 }
911 Sub => {
912 let y = stack.pop().ok_or(ExprError::EmptyStack)?;
913 let x = stack.pop().ok_or(ExprError::EmptyStack)?;
914 stack.push(x - y);
915 }
916 Store => {
917 let x = stack[stack.len() - 1];
918 cache.push(x);
919 }
920 Load(i) => stack.push(cache[*i]),
921 SkipIf(feature, count) => {
922 if feature.is_enabled() {
923 skip_count = *count;
924 stack.push(F::zero());
925 }
926 }
927 SkipIfNot(feature, count) => {
928 if !feature.is_enabled() {
929 skip_count = *count;
930 stack.push(F::zero());
931 }
932 }
933 }
934 }
935
936 assert_eq!(stack.len(), 1);
937 Ok(stack[0])
938 }
939}
940
941impl<C, Column> Expr<C, Column> {
942 pub fn cell(col: Column, row: CurrOrNext) -> Expr<C, Column> {
944 Expr::Atom(ExprInner::Cell(Variable { col, row }))
945 }
946
947 pub fn double(self) -> Self {
948 Expr::Double(Box::new(self))
949 }
950
951 pub fn square(self) -> Self {
952 Expr::Square(Box::new(self))
953 }
954
955 pub fn constant(c: C) -> Expr<C, Column> {
957 Expr::Atom(ExprInner::Constant(c))
958 }
959
960 pub fn degree(&self, d1_size: u64, zk_rows: u64) -> u64 {
969 use ExprInner::*;
970 use Operations::*;
971 match self {
972 Double(x) => x.degree(d1_size, zk_rows),
973 Atom(Constant(_)) => 0,
974 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => zk_rows + 1,
975 Atom(UnnormalizedLagrangeBasis(_)) => d1_size,
976 Atom(Cell(_)) => d1_size,
977 Square(x) => 2 * x.degree(d1_size, zk_rows),
978 Mul(x, y) => (*x).degree(d1_size, zk_rows) + (*y).degree(d1_size, zk_rows),
979 Add(x, y) | Sub(x, y) => {
980 core::cmp::max((*x).degree(d1_size, zk_rows), (*y).degree(d1_size, zk_rows))
981 }
982 Pow(e, d) => d * e.degree(d1_size, zk_rows),
983 Cache(_, e) => e.degree(d1_size, zk_rows),
984 IfFeature(_, e1, e2) => {
985 core::cmp::max(e1.degree(d1_size, zk_rows), e2.degree(d1_size, zk_rows))
986 }
987 }
988 }
989}
990
991impl<'a, F, Column: FormattedOutput + Debug + Clone, ChallengeTerm> fmt::Display
992 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
993where
994 F: PrimeField,
995 ChallengeTerm: AlphaChallengeTerm<'a>,
996{
997 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
998 let cache = &mut HashMap::new();
999 write!(f, "{}", self.text(cache))
1000 }
1001}
1002
1003#[derive(Clone)]
1004enum EvalResult<'a, F: FftField> {
1005 Constant(F),
1006 Evals {
1007 domain: Domain,
1008 evals: Evaluations<F, D<F>>,
1009 },
1010 SubEvals {
1015 domain: Domain,
1016 shift: usize,
1017 evals: &'a Evaluations<F, D<F>>,
1018 },
1019}
1020
1021fn unnormalized_lagrange_evals<
1056 'a,
1057 F: FftField,
1058 ChallengeTerm,
1059 Challenge: Index<ChallengeTerm, Output = F>,
1060 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge>,
1061>(
1062 l0_1: F,
1063 i: i32,
1064 res_domain: Domain,
1065 env: &Environment,
1066) -> Evaluations<F, D<F>> {
1067 let k = match res_domain {
1068 Domain::D1 => 1,
1069 Domain::D2 => 2,
1070 Domain::D4 => 4,
1071 Domain::D8 => 8,
1072 };
1073 let res_domain = env.get_domain(res_domain);
1074
1075 let d1 = env.get_domain(Domain::D1);
1076 let n = d1.size;
1077 let i = if i < 0 {
1079 ((i as isize) + (n as isize)) as usize
1080 } else {
1081 i as usize
1082 };
1083 let ii = i as u64;
1084 assert!(ii < n);
1085 let omega = d1.group_gen;
1086 let omega_i = omega.pow([ii]);
1087 let omega_minus_i = omega.pow([n - ii]);
1088
1089 let omega_k_n_pows = pows(k, res_domain.group_gen.pow([n]));
1094 let omega_k_pows = pows(k, res_domain.group_gen);
1095
1096 let mut evals: Vec<F> = {
1097 let mut v = vec![F::one(); k * (n as usize)];
1098 let mut omega_q = F::one();
1099 for q in 0..(n as usize) {
1100 for r in 1..k {
1102 v[k * q + r] = omega_q * omega_k_pows[r] - omega_i;
1103 }
1104 omega_q *= omega;
1105 }
1106 ark_ff::fields::batch_inversion::<F>(&mut v[..]);
1107 v
1108 };
1109 for q in 0..(n as usize) {
1115 evals[k * q] = F::zero();
1116 }
1117 evals[k * i] = omega_minus_i * l0_1;
1118
1119 for q in 0..(n as usize) {
1121 for r in 1..k {
1122 evals[k * q + r] *= omega_k_n_pows[r] - F::one();
1123 }
1124 }
1125
1126 Evaluations::<F, D<F>>::from_vec_and_domain(evals, res_domain)
1127}
1128
1129impl<'a, F: FftField> EvalResult<'a, F> {
1132 fn init_<G: Sync + Send + Fn(usize) -> F>(
1142 res_domain: (Domain, D<F>),
1143 g: G,
1144 ) -> Evaluations<F, D<F>> {
1145 let n = res_domain.1.size();
1146 Evaluations::<F, D<F>>::from_vec_and_domain(
1147 o1_utils::cfg_into_iter!(0..n).map(g).collect(),
1148 res_domain.1,
1149 )
1150 }
1151
1152 fn init<G: Sync + Send + Fn(usize) -> F>(res_domain: (Domain, D<F>), g: G) -> Self {
1155 Self::Evals {
1156 domain: res_domain.0,
1157 evals: Self::init_(res_domain, g),
1158 }
1159 }
1160
1161 fn add<'c>(self, other: EvalResult<'_, F>, res_domain: (Domain, D<F>)) -> EvalResult<'c, F> {
1162 use EvalResult::*;
1163 match (self, other) {
1164 (Constant(x), Constant(y)) => Constant(x + y),
1165 (Evals { domain, mut evals }, Constant(x))
1166 | (Constant(x), Evals { domain, mut evals }) => {
1167 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| *e += x);
1168 Evals { domain, evals }
1169 }
1170 (
1171 SubEvals {
1172 evals,
1173 domain,
1174 shift,
1175 },
1176 Constant(x),
1177 )
1178 | (
1179 Constant(x),
1180 SubEvals {
1181 evals,
1182 domain,
1183 shift,
1184 },
1185 ) => {
1186 let n = res_domain.1.size();
1187 let scale = (domain as usize) / (res_domain.0 as usize);
1188 assert!(
1189 scale != 0,
1190 "Check that the implementation of
1191 column_domain and the evaluation domain of the
1192 witnesses are the same"
1193 );
1194 let v: Vec<_> = o1_utils::cfg_into_iter!(0..n)
1195 .map(|i| {
1196 x + evals.evals[(scale * i + (domain as usize) * shift) % evals.evals.len()]
1197 })
1198 .collect();
1199 Evals {
1200 domain: res_domain.0,
1201 evals: Evaluations::<F, D<F>>::from_vec_and_domain(v, res_domain.1),
1202 }
1203 }
1204 (
1205 Evals {
1206 domain: d1,
1207 evals: mut es1,
1208 },
1209 Evals {
1210 domain: d2,
1211 evals: es2,
1212 },
1213 ) => {
1214 assert_eq!(d1, d2);
1215 es1 += &es2;
1216 Evals {
1217 domain: d1,
1218 evals: es1,
1219 }
1220 }
1221 (
1222 SubEvals {
1223 domain: d_sub,
1224 shift: s,
1225 evals: es_sub,
1226 },
1227 Evals {
1228 domain: d,
1229 mut evals,
1230 },
1231 )
1232 | (
1233 Evals {
1234 domain: d,
1235 mut evals,
1236 },
1237 SubEvals {
1238 domain: d_sub,
1239 shift: s,
1240 evals: es_sub,
1241 },
1242 ) => {
1243 let scale = (d_sub as usize) / (d as usize);
1244 assert!(
1245 scale != 0,
1246 "Check that the implementation of
1247 column_domain and the evaluation domain of the
1248 witnesses are the same"
1249 );
1250 o1_utils::cfg_iter_mut!(evals.evals)
1251 .enumerate()
1252 .for_each(|(i, e)| {
1253 *e += es_sub.evals[(scale * i + (d_sub as usize) * s) % es_sub.evals.len()];
1254 });
1255 Evals { evals, domain: d }
1256 }
1257 (
1258 SubEvals {
1259 domain: d1,
1260 shift: s1,
1261 evals: es1,
1262 },
1263 SubEvals {
1264 domain: d2,
1265 shift: s2,
1266 evals: es2,
1267 },
1268 ) => {
1269 let scale1 = (d1 as usize) / (res_domain.0 as usize);
1270 assert!(
1271 scale1 != 0,
1272 "Check that the implementation of
1273 column_domain and the evaluation domain of the
1274 witnesses are the same"
1275 );
1276 let scale2 = (d2 as usize) / (res_domain.0 as usize);
1277 assert!(
1278 scale2 != 0,
1279 "Check that the implementation of
1280 column_domain and the evaluation domain of the
1281 witnesses are the same"
1282 );
1283 let n = res_domain.1.size();
1284 let v: Vec<_> = o1_utils::cfg_into_iter!(0..n)
1285 .map(|i| {
1286 es1.evals[(scale1 * i + (d1 as usize) * s1) % es1.evals.len()]
1287 + es2.evals[(scale2 * i + (d2 as usize) * s2) % es2.evals.len()]
1288 })
1289 .collect();
1290
1291 Evals {
1292 domain: res_domain.0,
1293 evals: Evaluations::<F, D<F>>::from_vec_and_domain(v, res_domain.1),
1294 }
1295 }
1296 }
1297 }
1298
1299 fn sub<'c>(self, other: EvalResult<'_, F>, res_domain: (Domain, D<F>)) -> EvalResult<'c, F> {
1300 use EvalResult::*;
1301 match (self, other) {
1302 (Constant(x), Constant(y)) => Constant(x - y),
1303 (Evals { domain, mut evals }, Constant(x)) => {
1304 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| *e -= x);
1305 Evals { domain, evals }
1306 }
1307 (Constant(x), Evals { domain, mut evals }) => {
1308 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| *e = x - *e);
1309 Evals { domain, evals }
1310 }
1311 (
1312 SubEvals {
1313 evals,
1314 domain: d,
1315 shift: s,
1316 },
1317 Constant(x),
1318 ) => {
1319 let scale = (d as usize) / (res_domain.0 as usize);
1320 assert!(
1321 scale != 0,
1322 "Check that the implementation of
1323 column_domain and the evaluation domain of the
1324 witnesses are the same"
1325 );
1326 EvalResult::init(res_domain, |i| {
1327 evals.evals[(scale * i + (d as usize) * s) % evals.evals.len()] - x
1328 })
1329 }
1330 (
1331 Constant(x),
1332 SubEvals {
1333 evals,
1334 domain: d,
1335 shift: s,
1336 },
1337 ) => {
1338 let scale = (d as usize) / (res_domain.0 as usize);
1339 assert!(
1340 scale != 0,
1341 "Check that the implementation of
1342 column_domain and the evaluation domain of the
1343 witnesses are the same"
1344 );
1345
1346 EvalResult::init(res_domain, |i| {
1347 x - evals.evals[(scale * i + (d as usize) * s) % evals.evals.len()]
1348 })
1349 }
1350 (
1351 Evals {
1352 domain: d1,
1353 evals: mut es1,
1354 },
1355 Evals {
1356 domain: d2,
1357 evals: es2,
1358 },
1359 ) => {
1360 assert_eq!(d1, d2);
1361 es1 -= &es2;
1362 Evals {
1363 domain: d1,
1364 evals: es1,
1365 }
1366 }
1367 (
1368 SubEvals {
1369 domain: d_sub,
1370 shift: s,
1371 evals: es_sub,
1372 },
1373 Evals {
1374 domain: d,
1375 mut evals,
1376 },
1377 ) => {
1378 let scale = (d_sub as usize) / (d as usize);
1379 assert!(
1380 scale != 0,
1381 "Check that the implementation of
1382 column_domain and the evaluation domain of the
1383 witnesses are the same"
1384 );
1385
1386 o1_utils::cfg_iter_mut!(evals.evals)
1387 .enumerate()
1388 .for_each(|(i, e)| {
1389 *e = es_sub.evals[(scale * i + (d_sub as usize) * s) % es_sub.evals.len()]
1390 - *e;
1391 });
1392 Evals { evals, domain: d }
1393 }
1394 (
1395 Evals {
1396 domain: d,
1397 mut evals,
1398 },
1399 SubEvals {
1400 domain: d_sub,
1401 shift: s,
1402 evals: es_sub,
1403 },
1404 ) => {
1405 let scale = (d_sub as usize) / (d as usize);
1406 assert!(
1407 scale != 0,
1408 "Check that the implementation of
1409 column_domain and the evaluation domain of the
1410 witnesses are the same"
1411 );
1412 o1_utils::cfg_iter_mut!(evals.evals)
1413 .enumerate()
1414 .for_each(|(i, e)| {
1415 *e -= es_sub.evals[(scale * i + (d_sub as usize) * s) % es_sub.evals.len()];
1416 });
1417 Evals { evals, domain: d }
1418 }
1419 (
1420 SubEvals {
1421 domain: d1,
1422 shift: s1,
1423 evals: es1,
1424 },
1425 SubEvals {
1426 domain: d2,
1427 shift: s2,
1428 evals: es2,
1429 },
1430 ) => {
1431 let scale1 = (d1 as usize) / (res_domain.0 as usize);
1432 assert!(
1433 scale1 != 0,
1434 "Check that the implementation of
1435 column_domain and the evaluation domain of the
1436 witnesses are the same"
1437 );
1438 let scale2 = (d2 as usize) / (res_domain.0 as usize);
1439 assert!(
1440 scale2 != 0,
1441 "Check that the implementation of
1442 column_domain and the evaluation domain of the
1443 witnesses are the same"
1444 );
1445
1446 EvalResult::init(res_domain, |i| {
1447 es1.evals[(scale1 * i + (d1 as usize) * s1) % es1.evals.len()]
1448 - es2.evals[(scale2 * i + (d2 as usize) * s2) % es2.evals.len()]
1449 })
1450 }
1451 }
1452 }
1453
1454 fn pow<'b>(self, d: u64, res_domain: (Domain, D<F>)) -> EvalResult<'b, F> {
1455 let mut acc = EvalResult::Constant(F::one());
1456 for i in (0..u64::BITS).rev() {
1457 acc = acc.square(res_domain);
1458
1459 if (d >> i) & 1 == 1 {
1460 acc = acc.mul(self.clone(), res_domain)
1462 }
1463 }
1464 acc
1465 }
1466
1467 fn square<'b>(self, res_domain: (Domain, D<F>)) -> EvalResult<'b, F> {
1468 use EvalResult::*;
1469 match self {
1470 Constant(x) => Constant(x.square()),
1471 Evals { domain, mut evals } => {
1472 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| {
1473 e.square_in_place();
1474 });
1475 Evals { domain, evals }
1476 }
1477 SubEvals {
1478 evals,
1479 domain: d,
1480 shift: s,
1481 } => {
1482 let scale = (d as usize) / (res_domain.0 as usize);
1483 assert!(
1484 scale != 0,
1485 "Check that the implementation of
1486 column_domain and the evaluation domain of the
1487 witnesses are the same"
1488 );
1489 EvalResult::init(res_domain, |i| {
1490 evals.evals[(scale * i + (d as usize) * s) % evals.evals.len()].square()
1491 })
1492 }
1493 }
1494 }
1495
1496 fn mul<'c>(self, other: EvalResult<'_, F>, res_domain: (Domain, D<F>)) -> EvalResult<'c, F> {
1497 use EvalResult::*;
1498 match (self, other) {
1499 (Constant(x), Constant(y)) => Constant(x * y),
1500 (Evals { domain, mut evals }, Constant(x))
1501 | (Constant(x), Evals { domain, mut evals }) => {
1502 o1_utils::cfg_iter_mut!(evals.evals).for_each(|e| *e *= x);
1503 Evals { domain, evals }
1504 }
1505 (
1506 SubEvals {
1507 evals,
1508 domain: d,
1509 shift: s,
1510 },
1511 Constant(x),
1512 )
1513 | (
1514 Constant(x),
1515 SubEvals {
1516 evals,
1517 domain: d,
1518 shift: s,
1519 },
1520 ) => {
1521 let scale = (d as usize) / (res_domain.0 as usize);
1522 assert!(
1523 scale != 0,
1524 "Check that the implementation of
1525 column_domain and the evaluation domain of the
1526 witnesses are the same"
1527 );
1528 EvalResult::init(res_domain, |i| {
1529 x * evals.evals[(scale * i + (d as usize) * s) % evals.evals.len()]
1530 })
1531 }
1532 (
1533 Evals {
1534 domain: d1,
1535 evals: mut es1,
1536 },
1537 Evals {
1538 domain: d2,
1539 evals: es2,
1540 },
1541 ) => {
1542 assert_eq!(d1, d2);
1543 es1 *= &es2;
1544 Evals {
1545 domain: d1,
1546 evals: es1,
1547 }
1548 }
1549 (
1550 SubEvals {
1551 domain: d_sub,
1552 shift: s,
1553 evals: es_sub,
1554 },
1555 Evals {
1556 domain: d,
1557 mut evals,
1558 },
1559 )
1560 | (
1561 Evals {
1562 domain: d,
1563 mut evals,
1564 },
1565 SubEvals {
1566 domain: d_sub,
1567 shift: s,
1568 evals: es_sub,
1569 },
1570 ) => {
1571 let scale = (d_sub as usize) / (d as usize);
1572 assert!(
1573 scale != 0,
1574 "Check that the implementation of
1575 column_domainand the evaluation domain of the
1576 witnesses are the same"
1577 );
1578
1579 o1_utils::cfg_iter_mut!(evals.evals)
1580 .enumerate()
1581 .for_each(|(i, e)| {
1582 *e *= es_sub.evals[(scale * i + (d_sub as usize) * s) % es_sub.evals.len()];
1583 });
1584 Evals { evals, domain: d }
1585 }
1586 (
1587 SubEvals {
1588 domain: d1,
1589 shift: s1,
1590 evals: es1,
1591 },
1592 SubEvals {
1593 domain: d2,
1594 shift: s2,
1595 evals: es2,
1596 },
1597 ) => {
1598 let scale1 = (d1 as usize) / (res_domain.0 as usize);
1599 assert!(
1600 scale1 != 0,
1601 "Check that the implementation of
1602 column_domain and the evaluation domain of the
1603 witnesses are the same"
1604 );
1605 let scale2 = (d2 as usize) / (res_domain.0 as usize);
1606
1607 assert!(
1608 scale2 != 0,
1609 "Check that the implementation of
1610 column_domain and the evaluation domain of the
1611 witnesses are the same"
1612 );
1613 EvalResult::init(res_domain, |i| {
1614 es1.evals[(scale1 * i + (d1 as usize) * s1) % es1.evals.len()]
1615 * es2.evals[(scale2 * i + (d2 as usize) * s2) % es2.evals.len()]
1616 })
1617 }
1618 }
1619 }
1620}
1621
1622impl<'a, F: Field, Column: PartialEq + Copy, ChallengeTerm: AlphaChallengeTerm<'a>>
1623 Expr<ConstantExpr<F, ChallengeTerm>, Column>
1624{
1625 pub fn literal(x: F) -> Self {
1628 ConstantTerm::Literal(x).into()
1629 }
1630
1631 pub fn combine_constraints(alphas: impl Iterator<Item = u32>, cs: Vec<Self>) -> Self {
1634 let zero = Expr::<ConstantExpr<F, ChallengeTerm>, Column>::zero();
1635 cs.into_iter()
1636 .zip_eq(alphas)
1637 .map(|(c, i)| Expr::from(ConstantExpr::pow(ChallengeTerm::ALPHA.into(), i as u64)) * c)
1638 .fold(zero, |acc, x| acc + x)
1639 }
1640}
1641
1642impl<F: FftField, Column: Copy, ChallengeTerm: Copy> Expr<ConstantExpr<F, ChallengeTerm>, Column> {
1643 pub fn to_polish(&self) -> Vec<PolishToken<F, Column, ChallengeTerm>> {
1645 let mut res = vec![];
1646 let mut cache = HashMap::new();
1647 self.to_polish_(&mut cache, &mut res);
1648 res
1649 }
1650
1651 fn to_polish_(
1652 &self,
1653 cache: &mut HashMap<CacheId, usize>,
1654 res: &mut Vec<PolishToken<F, Column, ChallengeTerm>>,
1655 ) {
1656 match self {
1657 Expr::Double(x) => {
1658 x.to_polish_(cache, res);
1659 res.push(PolishToken::Dup);
1660 res.push(PolishToken::Add);
1661 }
1662 Expr::Square(x) => {
1663 x.to_polish_(cache, res);
1664 res.push(PolishToken::Dup);
1665 res.push(PolishToken::Mul);
1666 }
1667 Expr::Pow(x, d) => {
1668 x.to_polish_(cache, res);
1669 res.push(PolishToken::Pow(*d))
1670 }
1671 Expr::Atom(ExprInner::Constant(c)) => {
1672 c.to_polish(cache, res);
1673 }
1674 Expr::Atom(ExprInner::Cell(v)) => res.push(PolishToken::Cell(*v)),
1675 Expr::Atom(ExprInner::VanishesOnZeroKnowledgeAndPreviousRows) => {
1676 res.push(PolishToken::VanishesOnZeroKnowledgeAndPreviousRows);
1677 }
1678 Expr::Atom(ExprInner::UnnormalizedLagrangeBasis(i)) => {
1679 res.push(PolishToken::UnnormalizedLagrangeBasis(*i));
1680 }
1681 Expr::Add(x, y) => {
1682 x.to_polish_(cache, res);
1683 y.to_polish_(cache, res);
1684 res.push(PolishToken::Add);
1685 }
1686 Expr::Sub(x, y) => {
1687 x.to_polish_(cache, res);
1688 y.to_polish_(cache, res);
1689 res.push(PolishToken::Sub);
1690 }
1691 Expr::Mul(x, y) => {
1692 x.to_polish_(cache, res);
1693 y.to_polish_(cache, res);
1694 res.push(PolishToken::Mul);
1695 }
1696 Expr::Cache(id, e) => {
1697 match cache.get(id) {
1698 Some(pos) =>
1699 {
1701 res.push(PolishToken::Load(*pos))
1702 }
1703 None => {
1704 e.to_polish_(cache, res);
1706 res.push(PolishToken::Store);
1707 cache.insert(*id, cache.len());
1708 }
1709 }
1710 }
1711 Expr::IfFeature(feature, e1, e2) => {
1712 {
1713 let tok = PolishToken::SkipIfNot(*feature, 0);
1715 res.push(tok);
1716 let len_before = res.len();
1717 let mut cache = cache.clone();
1720 e1.to_polish_(&mut cache, res);
1721 let len_after = res.len();
1722 res[len_before - 1] = PolishToken::SkipIfNot(*feature, len_after - len_before);
1723 }
1724
1725 {
1726 let tok = PolishToken::SkipIfNot(*feature, 0);
1728 res.push(tok);
1729 let len_before = res.len();
1730 let mut cache = cache.clone();
1733 e2.to_polish_(&mut cache, res);
1734 let len_after = res.len();
1735 res[len_before - 1] = PolishToken::SkipIfNot(*feature, len_after - len_before);
1736 }
1737 }
1738 }
1739 }
1740}
1741
1742impl<F: FftField, Column: PartialEq + Copy, ChallengeTerm: Copy>
1743 Expr<ConstantExpr<F, ChallengeTerm>, Column>
1744{
1745 fn evaluate_constants_(
1746 &self,
1747 c: &Constants<F>,
1748 chals: &dyn Index<ChallengeTerm, Output = F>,
1749 ) -> Expr<F, Column> {
1750 use ExprInner::*;
1751 use Operations::*;
1752 match self {
1754 Double(x) => x.evaluate_constants_(c, chals).double(),
1755 Pow(x, d) => x.evaluate_constants_(c, chals).pow(*d),
1756 Square(x) => x.evaluate_constants_(c, chals).square(),
1757 Atom(Constant(x)) => Atom(Constant(x.value(c, chals))),
1758 Atom(Cell(v)) => Atom(Cell(*v)),
1759 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
1760 Atom(VanishesOnZeroKnowledgeAndPreviousRows)
1761 }
1762 Atom(UnnormalizedLagrangeBasis(i)) => Atom(UnnormalizedLagrangeBasis(*i)),
1763 Add(x, y) => x.evaluate_constants_(c, chals) + y.evaluate_constants_(c, chals),
1764 Mul(x, y) => x.evaluate_constants_(c, chals) * y.evaluate_constants_(c, chals),
1765 Sub(x, y) => x.evaluate_constants_(c, chals) - y.evaluate_constants_(c, chals),
1766 Cache(id, e) => Cache(*id, Box::new(e.evaluate_constants_(c, chals))),
1767 IfFeature(feature, e1, e2) => IfFeature(
1768 *feature,
1769 Box::new(e1.evaluate_constants_(c, chals)),
1770 Box::new(e2.evaluate_constants_(c, chals)),
1771 ),
1772 }
1773 }
1774
1775 pub fn evaluate<
1777 'a,
1778 Evaluations: ColumnEvaluations<F, Column = Column>,
1779 Challenge: Index<ChallengeTerm, Output = F>,
1780 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1781 >(
1782 &self,
1783 d: D<F>,
1784 pt: F,
1785 evals: &Evaluations,
1786 env: &Environment,
1787 ) -> Result<F, ExprError<Column>> {
1788 self.evaluate_(d, pt, evals, env.get_constants(), env.get_challenges())
1789 }
1790
1791 pub fn evaluate_<Evaluations: ColumnEvaluations<F, Column = Column>>(
1793 &self,
1794 d: D<F>,
1795 pt: F,
1796 evals: &Evaluations,
1797 c: &Constants<F>,
1798 chals: &dyn Index<ChallengeTerm, Output = F>,
1799 ) -> Result<F, ExprError<Column>> {
1800 use ExprInner::*;
1801 use Operations::*;
1802 match self {
1803 Double(x) => x.evaluate_(d, pt, evals, c, chals).map(|x| x.double()),
1804 Atom(Constant(x)) => Ok(x.value(c, chals)),
1805 Pow(x, p) => Ok(x.evaluate_(d, pt, evals, c, chals)?.pow([*p])),
1806 Mul(x, y) => {
1807 let x = (*x).evaluate_(d, pt, evals, c, chals)?;
1808 let y = (*y).evaluate_(d, pt, evals, c, chals)?;
1809 Ok(x * y)
1810 }
1811 Square(x) => Ok(x.evaluate_(d, pt, evals, c, chals)?.square()),
1812 Add(x, y) => {
1813 let x = (*x).evaluate_(d, pt, evals, c, chals)?;
1814 let y = (*y).evaluate_(d, pt, evals, c, chals)?;
1815 Ok(x + y)
1816 }
1817 Sub(x, y) => {
1818 let x = (*x).evaluate_(d, pt, evals, c, chals)?;
1819 let y = (*y).evaluate_(d, pt, evals, c, chals)?;
1820 Ok(x - y)
1821 }
1822 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
1823 Ok(eval_vanishes_on_last_n_rows(d, c.zk_rows + 1, pt))
1824 }
1825 Atom(UnnormalizedLagrangeBasis(i)) => {
1826 let offset = if i.zk_rows {
1827 -(c.zk_rows as i32) + i.offset
1828 } else {
1829 i.offset
1830 };
1831 Ok(unnormalized_lagrange_basis(&d, offset, &pt))
1832 }
1833 Atom(Cell(v)) => v.evaluate(evals),
1834 Cache(_, e) => e.evaluate_(d, pt, evals, c, chals),
1835 IfFeature(feature, e1, e2) => {
1836 if feature.is_enabled() {
1837 e1.evaluate_(d, pt, evals, c, chals)
1838 } else {
1839 e2.evaluate_(d, pt, evals, c, chals)
1840 }
1841 }
1842 }
1843 }
1844
1845 pub fn evaluate_constants<
1847 'a,
1848 Challenge: Index<ChallengeTerm, Output = F>,
1849 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1850 >(
1851 &self,
1852 env: &Environment,
1853 ) -> Expr<F, Column> {
1854 self.evaluate_constants_(env.get_constants(), env.get_challenges())
1855 }
1856
1857 pub fn evaluations<
1864 'a,
1865 Challenge: Index<ChallengeTerm, Output = F>,
1866 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1867 >(
1868 &self,
1869 env: &Environment,
1870 ) -> Evaluations<F, D<F>> {
1871 self.evaluate_constants(env).evaluations(env)
1872 }
1873}
1874
1875enum Either<A, B> {
1879 Left(A),
1880 Right(B),
1881}
1882
1883fn fused_eval_mode() -> u8 {
1887 #[cfg(feature = "std")]
1888 {
1889 match std::env::var("KIMCHI_FUSED_EVAL").as_deref() {
1890 Ok("verify") => 2,
1891 Ok("1") | Ok("use") => 1,
1892 _ => 0,
1893 }
1894 }
1895 #[cfg(not(feature = "std"))]
1896 {
1897 0
1898 }
1899}
1900
1901enum FOp {
1905 Lit(usize),
1906 Col(usize),
1907 Aux(usize),
1908 Add,
1909 Sub,
1910 Mul,
1911 Square,
1912 Double,
1913 Pow(u64),
1914 StoreReg(usize),
1915 LoadReg(usize),
1916}
1917
1918struct FCol<'a, F> {
1921 evals: &'a [F],
1922 scale: usize,
1923 offset: usize,
1924 len: usize,
1925}
1926
1927impl<F: FftField, Column: Copy> Expr<F, Column> {
1928 pub fn evaluate<Evaluations: ColumnEvaluations<F, Column = Column>>(
1930 &self,
1931 d: D<F>,
1932 pt: F,
1933 zk_rows: u64,
1934 evals: &Evaluations,
1935 ) -> Result<F, ExprError<Column>> {
1936 use ExprInner::*;
1937 use Operations::*;
1938 match self {
1939 Atom(Constant(x)) => Ok(*x),
1940 Pow(x, p) => Ok(x.evaluate(d, pt, zk_rows, evals)?.pow([*p])),
1941 Double(x) => x.evaluate(d, pt, zk_rows, evals).map(|x| x.double()),
1942 Square(x) => x.evaluate(d, pt, zk_rows, evals).map(|x| x.square()),
1943 Mul(x, y) => {
1944 let x = (*x).evaluate(d, pt, zk_rows, evals)?;
1945 let y = (*y).evaluate(d, pt, zk_rows, evals)?;
1946 Ok(x * y)
1947 }
1948 Add(x, y) => {
1949 let x = (*x).evaluate(d, pt, zk_rows, evals)?;
1950 let y = (*y).evaluate(d, pt, zk_rows, evals)?;
1951 Ok(x + y)
1952 }
1953 Sub(x, y) => {
1954 let x = (*x).evaluate(d, pt, zk_rows, evals)?;
1955 let y = (*y).evaluate(d, pt, zk_rows, evals)?;
1956 Ok(x - y)
1957 }
1958 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
1959 Ok(eval_vanishes_on_last_n_rows(d, zk_rows + 1, pt))
1960 }
1961 Atom(UnnormalizedLagrangeBasis(i)) => {
1962 let offset = if i.zk_rows {
1963 -(zk_rows as i32) + i.offset
1964 } else {
1965 i.offset
1966 };
1967 Ok(unnormalized_lagrange_basis(&d, offset, &pt))
1968 }
1969 Atom(Cell(v)) => v.evaluate(evals),
1970 Cache(_, e) => e.evaluate(d, pt, zk_rows, evals),
1971 IfFeature(feature, e1, e2) => {
1972 if feature.is_enabled() {
1973 e1.evaluate(d, pt, zk_rows, evals)
1974 } else {
1975 e2.evaluate(d, pt, zk_rows, evals)
1976 }
1977 }
1978 }
1979 }
1980
1981 pub fn evaluations<
1983 'a,
1984 ChallengeTerm,
1985 Challenge: Index<ChallengeTerm, Output = F>,
1986 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
1987 >(
1988 &self,
1989 env: &Environment,
1990 ) -> Evaluations<F, D<F>> {
1991 if fused_eval_mode() == 1 {
1992 if let Some(f) = self.evaluations_fused(env) {
1993 return f;
1994 }
1995 }
1996 let d1_size = env.get_domain(Domain::D1).size;
1997 let deg = self.degree(d1_size, env.get_constants().zk_rows);
1998 let d = if deg <= d1_size {
1999 Domain::D1
2000 } else if deg <= 4 * d1_size {
2001 Domain::D4
2002 } else if deg <= 8 * d1_size {
2003 Domain::D8
2004 } else {
2005 panic!("constraint had degree {deg} > d8 ({})", 8 * d1_size);
2006 };
2007
2008 let mut cache = HashMap::new();
2009
2010 let evals = match self.evaluations_helper(&mut cache, d, env) {
2011 Either::Left(x) => x,
2012 Either::Right(id) => cache.get(&id).unwrap().clone(),
2013 };
2014
2015 let result = match evals {
2016 EvalResult::Evals { evals, domain } => {
2017 assert_eq!(domain, d);
2018 evals
2019 }
2020 EvalResult::Constant(x) => EvalResult::init_((d, env.get_domain(d)), |_| x),
2021 EvalResult::SubEvals {
2022 evals,
2023 domain: d_sub,
2024 shift: s,
2025 } => {
2026 let res_domain = env.get_domain(d);
2027 let scale = (d_sub as usize) / (d as usize);
2028 assert!(
2029 scale != 0,
2030 "Check that the implementation of
2031 column_domain and the evaluation domain of the
2032 witnesses are the same"
2033 );
2034 EvalResult::init_((d, res_domain), |i| {
2035 evals.evals[(scale * i + (d_sub as usize) * s) % evals.evals.len()]
2036 })
2037 }
2038 };
2039 if fused_eval_mode() == 2 {
2040 if let Some(f) = self.evaluations_fused(env) {
2041 assert_eq!(
2042 f.evals, result.evals,
2043 "fused evaluator disagrees with vectorised path"
2044 );
2045 }
2046 }
2047 result
2048 }
2049
2050 fn evaluations_fused<'a, ChallengeTerm, Challenge, Environment>(
2055 &self,
2056 env: &Environment,
2057 ) -> Option<Evaluations<F, D<F>>>
2058 where
2059 Challenge: Index<ChallengeTerm, Output = F>,
2060 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2061 {
2062 let d1_size = env.get_domain(Domain::D1).size;
2063 let deg = self.degree(d1_size, env.get_constants().zk_rows);
2064 let d = if deg <= d1_size {
2067 return None;
2068 } else if deg <= 4 * d1_size {
2069 Domain::D4
2070 } else if deg <= 8 * d1_size {
2071 Domain::D8
2072 } else {
2073 return None;
2074 };
2075
2076 let mut code: Vec<FOp> = Vec::new();
2077 let mut lits: Vec<F> = Vec::new();
2078 let mut cols: Vec<FCol<'a, F>> = Vec::new();
2079 let mut aux: Vec<Vec<F>> = Vec::new();
2080 let mut regs: HashMap<CacheId, usize> = HashMap::new();
2081 let mut n_reg = 0usize;
2082 let mut depth = 0usize;
2083 let mut max_depth = 0usize;
2084 self.fcompile(
2085 env,
2086 d,
2087 &mut code,
2088 &mut lits,
2089 &mut cols,
2090 &mut aux,
2091 &mut regs,
2092 &mut n_reg,
2093 &mut depth,
2094 &mut max_depth,
2095 )
2096 .ok()?;
2097
2098 let dom = env.get_domain(d);
2099 let n = dom.size as usize;
2100 let regbase = max_depth;
2101 let frame = (max_depth + n_reg).max(1);
2102 const B: usize = 8;
2107 let chunk = 1usize << 14; let lstart = if (d as usize) == 8 { 1usize } else { 0usize };
2118 let mut out = vec![F::zero(); n];
2119 o1_utils::cfg_chunks_mut!(out, chunk)
2120 .enumerate()
2121 .for_each(|(ci, slots)| {
2122 let base = ci * chunk;
2123 let mut st = vec![F::zero(); frame * B];
2124 let len = slots.len();
2125 let mut off = 0usize;
2126 while off < len {
2127 let blk = B.min(len - off);
2128 let idx0 = base + off;
2129 let mut sp = 0usize;
2130 unsafe {
2134 for op in &code {
2135 match op {
2136 FOp::Lit(i) => {
2137 let v = *lits.get_unchecked(*i);
2138 let d0 = sp * B;
2139 for l in lstart..blk {
2140 *st.get_unchecked_mut(d0 + l) = v;
2141 }
2142 sp += 1;
2143 }
2144 FOp::Col(i) => {
2145 let c = cols.get_unchecked(*i);
2146 let d0 = sp * B;
2147 for l in lstart..blk {
2148 let raw = c.scale * (idx0 + l) + c.offset;
2149 let j = if raw < c.len { raw } else { raw % c.len };
2150 *st.get_unchecked_mut(d0 + l) = *c.evals.get_unchecked(j);
2151 }
2152 sp += 1;
2153 }
2154 FOp::Aux(i) => {
2155 let a = aux.get_unchecked(*i);
2157 let d0 = sp * B;
2158 for l in lstart..blk {
2159 *st.get_unchecked_mut(d0 + l) = *a.get_unchecked(idx0 + l);
2160 }
2161 sp += 1;
2162 }
2163 FOp::Add => {
2164 sp -= 1;
2165 let (t0, s0) = ((sp - 1) * B, sp * B);
2166 for l in lstart..blk {
2167 let v = *st.get_unchecked(s0 + l);
2168 *st.get_unchecked_mut(t0 + l) += v;
2169 }
2170 }
2171 FOp::Sub => {
2172 sp -= 1;
2173 let (t0, s0) = ((sp - 1) * B, sp * B);
2174 for l in lstart..blk {
2175 let v = *st.get_unchecked(s0 + l);
2176 *st.get_unchecked_mut(t0 + l) -= v;
2177 }
2178 }
2179 FOp::Mul => {
2180 sp -= 1;
2181 let (t0, s0) = ((sp - 1) * B, sp * B);
2182 for l in lstart..blk {
2183 let v = *st.get_unchecked(s0 + l);
2184 *st.get_unchecked_mut(t0 + l) *= v;
2185 }
2186 }
2187 FOp::Square => {
2188 let t0 = (sp - 1) * B;
2189 for l in lstart..blk {
2190 st.get_unchecked_mut(t0 + l).square_in_place();
2191 }
2192 }
2193 FOp::Double => {
2194 let t0 = (sp - 1) * B;
2195 for l in lstart..blk {
2196 st.get_unchecked_mut(t0 + l).double_in_place();
2197 }
2198 }
2199 FOp::Pow(p) => {
2200 let t0 = (sp - 1) * B;
2201 if *p == 7 {
2202 for l in lstart..blk {
2207 let x = *st.get_unchecked(t0 + l);
2208 let x2 = x.square();
2209 let x4 = x2.square();
2210 *st.get_unchecked_mut(t0 + l) = x4 * x2 * x;
2211 }
2212 } else {
2213 for l in lstart..blk {
2214 let v = st.get_unchecked(t0 + l).pow([*p]);
2215 *st.get_unchecked_mut(t0 + l) = v;
2216 }
2217 }
2218 }
2219 FOp::StoreReg(r) => {
2220 let (d0, s0) = ((regbase + *r) * B, (sp - 1) * B);
2221 for l in lstart..blk {
2222 *st.get_unchecked_mut(d0 + l) = *st.get_unchecked(s0 + l);
2223 }
2224 }
2225 FOp::LoadReg(r) => {
2226 let (d0, s0) = (sp * B, (regbase + *r) * B);
2227 for l in lstart..blk {
2228 *st.get_unchecked_mut(d0 + l) = *st.get_unchecked(s0 + l);
2229 }
2230 sp += 1;
2231 }
2232 }
2233 }
2234 for l in lstart..blk {
2235 *slots.get_unchecked_mut(off + l) = *st.get_unchecked(l);
2236 }
2237 }
2238 off += blk;
2239 }
2240 });
2241
2242 Some(Evaluations::from_vec_and_domain(out, dom))
2243 }
2244
2245 #[allow(clippy::too_many_arguments)]
2248 fn fcompile<'a, ChallengeTerm, Challenge, Environment>(
2249 &self,
2250 env: &Environment,
2251 d: Domain,
2252 code: &mut Vec<FOp>,
2253 lits: &mut Vec<F>,
2254 cols: &mut Vec<FCol<'a, F>>,
2255 aux: &mut Vec<Vec<F>>,
2256 regs: &mut HashMap<CacheId, usize>,
2257 n_reg: &mut usize,
2258 depth: &mut usize,
2259 max_depth: &mut usize,
2260 ) -> Result<(), ()>
2261 where
2262 Challenge: Index<ChallengeTerm, Output = F>,
2263 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2264 {
2265 use ExprInner::*;
2266 use Operations::*;
2267 match self {
2268 Atom(Constant(x)) => {
2269 lits.push(*x);
2270 code.push(FOp::Lit(lits.len() - 1));
2271 *depth += 1;
2272 *max_depth = (*max_depth).max(*depth);
2273 }
2274 Atom(Cell(v)) => {
2275 match env.get_column(&v.col) {
2276 None => {
2277 lits.push(F::zero());
2278 code.push(FOp::Lit(lits.len() - 1));
2279 }
2280 Some(e) => {
2281 let d_sub = env.column_domain(&v.col) as usize;
2282 let scale = d_sub / (d as usize);
2283 if scale == 0 {
2284 return Err(());
2285 }
2286 cols.push(FCol {
2287 evals: e.evals.as_slice(),
2288 scale,
2289 offset: d_sub * v.row.shift(),
2290 len: e.evals.len(),
2291 });
2292 code.push(FOp::Col(cols.len() - 1));
2293 }
2294 }
2295 *depth += 1;
2296 *max_depth = (*max_depth).max(*depth);
2297 }
2298 Add(a, b) => {
2299 a.fcompile(env, d, code, lits, cols, aux, regs, n_reg, depth, max_depth)?;
2300 b.fcompile(env, d, code, lits, cols, aux, regs, n_reg, depth, max_depth)?;
2301 code.push(FOp::Add);
2302 *depth -= 1;
2303 }
2304 Sub(a, b) => {
2305 a.fcompile(env, d, code, lits, cols, aux, regs, n_reg, depth, max_depth)?;
2306 b.fcompile(env, d, code, lits, cols, aux, regs, n_reg, depth, max_depth)?;
2307 code.push(FOp::Sub);
2308 *depth -= 1;
2309 }
2310 Mul(a, b) => {
2311 a.fcompile(env, d, code, lits, cols, aux, regs, n_reg, depth, max_depth)?;
2312 b.fcompile(env, d, code, lits, cols, aux, regs, n_reg, depth, max_depth)?;
2313 code.push(FOp::Mul);
2314 *depth -= 1;
2315 }
2316 Square(a) => {
2317 a.fcompile(env, d, code, lits, cols, aux, regs, n_reg, depth, max_depth)?;
2318 code.push(FOp::Square);
2319 }
2320 Double(a) => {
2321 a.fcompile(env, d, code, lits, cols, aux, regs, n_reg, depth, max_depth)?;
2322 code.push(FOp::Double);
2323 }
2324 Pow(a, p) => {
2325 a.fcompile(env, d, code, lits, cols, aux, regs, n_reg, depth, max_depth)?;
2326 code.push(FOp::Pow(*p));
2327 }
2328 Cache(id, e) => match regs.get(id) {
2329 Some(&r) => {
2330 code.push(FOp::LoadReg(r));
2331 *depth += 1;
2332 *max_depth = (*max_depth).max(*depth);
2333 }
2334 None => {
2335 e.fcompile(env, d, code, lits, cols, aux, regs, n_reg, depth, max_depth)?;
2336 let r = *n_reg;
2337 *n_reg += 1;
2338 regs.insert(*id, r);
2339 code.push(FOp::StoreReg(r));
2340 }
2341 },
2342 IfFeature(feature, e1, e2) => {
2345 if feature.is_enabled() {
2346 e1.fcompile(env, d, code, lits, cols, aux, regs, n_reg, depth, max_depth)?;
2347 } else {
2348 e2.fcompile(env, d, code, lits, cols, aux, regs, n_reg, depth, max_depth)?;
2349 }
2350 }
2351 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
2353 let e = env.vanishes_on_zero_knowledge_and_previous_rows();
2354 let d_sub = Domain::D8 as usize;
2355 let scale = d_sub / (d as usize);
2356 if scale == 0 {
2357 return Err(());
2358 }
2359 cols.push(FCol {
2360 evals: e.evals.as_slice(),
2361 scale,
2362 offset: 0,
2363 len: e.evals.len(),
2364 });
2365 code.push(FOp::Col(cols.len() - 1));
2366 *depth += 1;
2367 *max_depth = (*max_depth).max(*depth);
2368 }
2369 Atom(UnnormalizedLagrangeBasis(i)) => {
2372 let offset = if i.zk_rows {
2373 -(env.get_constants().zk_rows as i32) + i.offset
2374 } else {
2375 i.offset
2376 };
2377 let evals = unnormalized_lagrange_evals(env.l0_1(), offset, d, env);
2378 aux.push(evals.evals);
2379 code.push(FOp::Aux(aux.len() - 1));
2380 *depth += 1;
2381 *max_depth = (*max_depth).max(*depth);
2382 }
2383 }
2384 Ok(())
2385 }
2386
2387 fn evaluations_helper<
2388 'a,
2389 'b,
2390 ChallengeTerm,
2391 Challenge: Index<ChallengeTerm, Output = F>,
2392 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2393 >(
2394 &self,
2395 cache: &'b mut HashMap<CacheId, EvalResult<'a, F>>,
2396 d: Domain,
2397 env: &Environment,
2398 ) -> Either<EvalResult<'a, F>, CacheId>
2399 where
2400 'a: 'b,
2401 {
2402 let dom = (d, env.get_domain(d));
2403
2404 let res: EvalResult<'a, F> = match self {
2405 Expr::Square(x) => match x.evaluations_helper(cache, d, env) {
2406 Either::Left(x) => x.square(dom),
2407 Either::Right(id) => id.get_from(cache).unwrap().square(dom),
2408 },
2409 Expr::Double(x) => {
2410 let x = x.evaluations_helper(cache, d, env);
2411 let res = match x {
2412 Either::Left(x) => {
2413 let x = match x {
2414 EvalResult::Evals { domain, mut evals } => {
2415 o1_utils::cfg_iter_mut!(evals.evals).for_each(|x| {
2416 x.double_in_place();
2417 });
2418 return Either::Left(EvalResult::Evals { domain, evals });
2419 }
2420 x => x,
2421 };
2422 let xx = || match &x {
2423 EvalResult::Constant(x) => EvalResult::Constant(*x),
2424 EvalResult::SubEvals {
2425 domain,
2426 shift,
2427 evals,
2428 } => EvalResult::SubEvals {
2429 domain: *domain,
2430 shift: *shift,
2431 evals,
2432 },
2433 EvalResult::Evals { domain, evals } => EvalResult::SubEvals {
2434 domain: *domain,
2435 shift: 0,
2436 evals,
2437 },
2438 };
2439 xx().add(xx(), dom)
2440 }
2441 Either::Right(id) => {
2442 let x1 = id.get_from(cache).unwrap();
2443 let x2 = id.get_from(cache).unwrap();
2444 x1.add(x2, dom)
2445 }
2446 };
2447 return Either::Left(res);
2448 }
2449 Expr::Cache(id, e) => match cache.get(id) {
2450 Some(_) => return Either::Right(*id),
2451 None => {
2452 match e.evaluations_helper(cache, d, env) {
2453 Either::Left(es) => {
2454 cache.insert(*id, es);
2455 }
2456 Either::Right(_) => {}
2457 };
2458 return Either::Right(*id);
2459 }
2460 },
2461 Expr::Pow(x, p) => {
2462 let x = x.evaluations_helper(cache, d, env);
2463 match x {
2464 Either::Left(x) => x.pow(*p, (d, env.get_domain(d))),
2465 Either::Right(id) => {
2466 id.get_from(cache).unwrap().pow(*p, (d, env.get_domain(d)))
2467 }
2468 }
2469 }
2470 Expr::Atom(ExprInner::VanishesOnZeroKnowledgeAndPreviousRows) => EvalResult::SubEvals {
2471 domain: Domain::D8,
2472 shift: 0,
2473 evals: env.vanishes_on_zero_knowledge_and_previous_rows(),
2474 },
2475 Expr::Atom(ExprInner::Constant(x)) => EvalResult::Constant(*x),
2476 Expr::Atom(ExprInner::UnnormalizedLagrangeBasis(i)) => {
2477 let offset = if i.zk_rows {
2478 -(env.get_constants().zk_rows as i32) + i.offset
2479 } else {
2480 i.offset
2481 };
2482 EvalResult::Evals {
2483 domain: d,
2484 evals: unnormalized_lagrange_evals(env.l0_1(), offset, d, env),
2485 }
2486 }
2487 Expr::Atom(ExprInner::Cell(Variable { col, row })) => {
2488 let evals: &'a Evaluations<F, D<F>> = {
2489 match env.get_column(col) {
2490 None => return Either::Left(EvalResult::Constant(F::zero())),
2491 Some(e) => e,
2492 }
2493 };
2494 EvalResult::SubEvals {
2495 domain: env.column_domain(col),
2496 shift: row.shift(),
2497 evals,
2498 }
2499 }
2500 Expr::Add(e1, e2) => {
2501 let dom = (d, env.get_domain(d));
2502 let f = |x: EvalResult<F>, y: EvalResult<F>| x.add(y, dom);
2503 let e1 = e1.evaluations_helper(cache, d, env);
2504 let e2 = e2.evaluations_helper(cache, d, env);
2505 use Either::*;
2506 match (e1, e2) {
2507 (Left(e1), Left(e2)) => f(e1, e2),
2508 (Right(id1), Left(e2)) => f(id1.get_from(cache).unwrap(), e2),
2509 (Left(e1), Right(id2)) => f(e1, id2.get_from(cache).unwrap()),
2510 (Right(id1), Right(id2)) => {
2511 f(id1.get_from(cache).unwrap(), id2.get_from(cache).unwrap())
2512 }
2513 }
2514 }
2515 Expr::Sub(e1, e2) => {
2516 let dom = (d, env.get_domain(d));
2517 let f = |x: EvalResult<F>, y: EvalResult<F>| x.sub(y, dom);
2518 let e1 = e1.evaluations_helper(cache, d, env);
2519 let e2 = e2.evaluations_helper(cache, d, env);
2520 use Either::*;
2521 match (e1, e2) {
2522 (Left(e1), Left(e2)) => f(e1, e2),
2523 (Right(id1), Left(e2)) => f(id1.get_from(cache).unwrap(), e2),
2524 (Left(e1), Right(id2)) => f(e1, id2.get_from(cache).unwrap()),
2525 (Right(id1), Right(id2)) => {
2526 f(id1.get_from(cache).unwrap(), id2.get_from(cache).unwrap())
2527 }
2528 }
2529 }
2530 Expr::Mul(e1, e2) => {
2531 let dom = (d, env.get_domain(d));
2532 let f = |x: EvalResult<F>, y: EvalResult<F>| x.mul(y, dom);
2533 let e1 = e1.evaluations_helper(cache, d, env);
2534 let e2 = e2.evaluations_helper(cache, d, env);
2535 use Either::*;
2536 match (e1, e2) {
2537 (Left(e1), Left(e2)) => f(e1, e2),
2538 (Right(id1), Left(e2)) => f(id1.get_from(cache).unwrap(), e2),
2539 (Left(e1), Right(id2)) => f(e1, id2.get_from(cache).unwrap()),
2540 (Right(id1), Right(id2)) => {
2541 f(id1.get_from(cache).unwrap(), id2.get_from(cache).unwrap())
2542 }
2543 }
2544 }
2545 Expr::IfFeature(feature, e1, e2) => {
2546 let mut cache = cache.clone();
2549 if feature.is_enabled() {
2550 return e1.evaluations_helper(&mut cache, d, env);
2551 } else {
2552 return e2.evaluations_helper(&mut cache, d, env);
2553 }
2554 }
2555 };
2556 Either::Left(res)
2557 }
2558}
2559
2560#[derive(Clone, Debug, Serialize, Deserialize)]
2561pub struct Linearization<E, Column> {
2564 pub constant_term: E,
2565 pub index_terms: Vec<(Column, E)>,
2566}
2567
2568impl<E: Default, Column> Default for Linearization<E, Column> {
2569 fn default() -> Self {
2570 Linearization {
2571 constant_term: E::default(),
2572 index_terms: vec![],
2573 }
2574 }
2575}
2576
2577impl<A, Column: Copy> Linearization<A, Column> {
2578 pub fn map<B, F: Fn(&A) -> B>(&self, f: F) -> Linearization<B, Column> {
2580 Linearization {
2581 constant_term: f(&self.constant_term),
2582 index_terms: self.index_terms.iter().map(|(c, x)| (*c, f(x))).collect(),
2583 }
2584 }
2585}
2586
2587impl<F: FftField, Column: PartialEq + Copy, ChallengeTerm: Copy>
2588 Linearization<Expr<ConstantExpr<F, ChallengeTerm>, Column>, Column>
2589{
2590 pub fn evaluate_constants<
2593 'a,
2594 Challenge: Index<ChallengeTerm, Output = F>,
2595 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2596 >(
2597 &self,
2598 env: &Environment,
2599 ) -> Linearization<Expr<F, Column>, Column> {
2600 self.map(|e| e.evaluate_constants(env))
2601 }
2602}
2603
2604impl<F: FftField, Column: Copy + Debug, ChallengeTerm: Copy>
2605 Linearization<Vec<PolishToken<F, Column, ChallengeTerm>>, Column>
2606{
2607 pub fn to_polynomial<
2610 'a,
2611 Challenge: Index<ChallengeTerm, Output = F>,
2612 ColEvaluations: ColumnEvaluations<F, Column = Column>,
2613 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2614 >(
2615 &self,
2616 env: &Environment,
2617 pt: F,
2618 evals: &ColEvaluations,
2619 ) -> (F, Evaluations<F, D<F>>) {
2620 let cs = env.get_constants();
2621 let chals = env.get_challenges();
2622 let d1 = env.get_domain(Domain::D1);
2623 let n = d1.size();
2624 let mut res = vec![F::zero(); n];
2625 self.index_terms.iter().for_each(|(idx, c)| {
2626 let c = PolishToken::evaluate(c, d1, pt, evals, cs, chals).unwrap();
2627 let e = env
2628 .get_column(idx)
2629 .unwrap_or_else(|| panic!("Index polynomial {idx:?} not found"));
2630 let scale = e.evals.len() / n;
2631 o1_utils::cfg_iter_mut!(res)
2632 .enumerate()
2633 .for_each(|(i, r)| *r += c * e.evals[scale * i]);
2634 });
2635 let p = Evaluations::<F, D<F>>::from_vec_and_domain(res, d1);
2636 (
2637 PolishToken::evaluate(&self.constant_term, d1, pt, evals, cs, chals).unwrap(),
2638 p,
2639 )
2640 }
2641}
2642
2643impl<F: FftField, Column: Debug + PartialEq + Copy, ChallengeTerm: Copy>
2644 Linearization<Expr<ConstantExpr<F, ChallengeTerm>, Column>, Column>
2645{
2646 pub fn to_polynomial<
2649 'a,
2650 Challenge: Index<ChallengeTerm, Output = F>,
2651 ColEvaluations: ColumnEvaluations<F, Column = Column>,
2652 Environment: ColumnEnvironment<'a, F, ChallengeTerm, Challenge, Column = Column>,
2653 >(
2654 &self,
2655 env: &Environment,
2656 pt: F,
2657 evals: &ColEvaluations,
2658 ) -> (F, DensePolynomial<F>) {
2659 let cs = env.get_constants();
2660 let chals = env.get_challenges();
2661 let d1 = env.get_domain(Domain::D1);
2662 let n = d1.size();
2663 let mut res = vec![F::zero(); n];
2664 self.index_terms.iter().for_each(|(idx, c)| {
2665 let c = c.evaluate_(d1, pt, evals, cs, chals).unwrap();
2666 let e = env
2667 .get_column(idx)
2668 .unwrap_or_else(|| panic!("Index polynomial {idx:?} not found"));
2669 let scale = e.evals.len() / n;
2670 o1_utils::cfg_iter_mut!(res)
2671 .enumerate()
2672 .for_each(|(i, r)| *r += c * e.evals[scale * i])
2673 });
2674 let p = Evaluations::<F, D<F>>::from_vec_and_domain(res, d1).interpolate();
2675 (
2676 self.constant_term
2677 .evaluate_(d1, pt, evals, cs, chals)
2678 .unwrap(),
2679 p,
2680 )
2681 }
2682}
2683
2684type Monomials<F, Column> = HashMap<Vec<Variable<Column>>, Expr<F, Column>>;
2685
2686fn mul_monomials<
2687 F: Neg<Output = F> + Clone + One + Zero + PartialEq,
2688 Column: Ord + Copy + core::hash::Hash,
2689>(
2690 e1: &Monomials<F, Column>,
2691 e2: &Monomials<F, Column>,
2692) -> Monomials<F, Column>
2693where
2694 ExprInner<F, Column>: Literal,
2695 <ExprInner<F, Column> as Literal>::F: Field,
2696{
2697 let mut res: HashMap<_, Expr<F, Column>> = HashMap::new();
2698 for (m1, c1) in e1.iter() {
2699 for (m2, c2) in e2.iter() {
2700 let mut m = m1.clone();
2701 m.extend(m2);
2702 m.sort();
2703 let c1c2 = c1.clone() * c2.clone();
2704 let v = res.entry(m).or_insert_with(Expr::<F, Column>::zero);
2705 *v = v.clone() + c1c2;
2706 }
2707 }
2708 res
2709}
2710
2711impl<
2712 F: Neg<Output = F> + Clone + One + Zero + PartialEq,
2713 Column: Ord + Copy + core::hash::Hash,
2714 > Expr<F, Column>
2715where
2716 ExprInner<F, Column>: Literal,
2717 <ExprInner<F, Column> as Literal>::F: Field,
2718{
2719 fn is_constant(&self, evaluated: &HashSet<Column>) -> bool {
2724 use ExprInner::*;
2725 use Operations::*;
2726 match self {
2727 Pow(x, _) => x.is_constant(evaluated),
2728 Square(x) => x.is_constant(evaluated),
2729 Atom(Constant(_)) => true,
2730 Atom(Cell(v)) => evaluated.contains(&v.col),
2731 Double(x) => x.is_constant(evaluated),
2732 Add(x, y) | Sub(x, y) | Mul(x, y) => {
2733 x.is_constant(evaluated) && y.is_constant(evaluated)
2734 }
2735 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => true,
2736 Atom(UnnormalizedLagrangeBasis(_)) => true,
2737 Cache(_, x) => x.is_constant(evaluated),
2738 IfFeature(_, e1, e2) => e1.is_constant(evaluated) && e2.is_constant(evaluated),
2739 }
2740 }
2741
2742 fn monomials(&self, ev: &HashSet<Column>) -> HashMap<Vec<Variable<Column>>, Expr<F, Column>> {
2743 let sing = |v: Vec<Variable<Column>>, c: Expr<F, Column>| {
2744 let mut h = HashMap::new();
2745 h.insert(v, c);
2746 h
2747 };
2748 let constant = |e: Expr<F, Column>| sing(vec![], e);
2749 use ExprInner::*;
2750 use Operations::*;
2751
2752 if self.is_constant(ev) {
2753 return constant(self.clone());
2754 }
2755
2756 match self {
2757 Pow(x, d) => {
2758 let mut acc = sing(vec![], Expr::<F, Column>::one());
2760 let mut acc_is_one = true;
2761 let x = x.monomials(ev);
2762
2763 for i in (0..u64::BITS).rev() {
2764 if !acc_is_one {
2765 let acc2 = mul_monomials(&acc, &acc);
2766 acc = acc2;
2767 }
2768
2769 if (d >> i) & 1 == 1 {
2770 let res = mul_monomials(&acc, &x);
2771 acc = res;
2772 acc_is_one = false;
2773 }
2774 }
2775 acc
2776 }
2777 Double(e) => {
2778 HashMap::from_iter(e.monomials(ev).into_iter().map(|(m, c)| (m, c.double())))
2779 }
2780 Cache(_, e) => e.monomials(ev),
2781 Atom(UnnormalizedLagrangeBasis(i)) => constant(Atom(UnnormalizedLagrangeBasis(*i))),
2782 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
2783 constant(Atom(VanishesOnZeroKnowledgeAndPreviousRows))
2784 }
2785 Atom(Constant(c)) => constant(Atom(Constant(c.clone()))),
2786 Atom(Cell(var)) => sing(vec![*var], Atom(Constant(F::one()))),
2787 Add(e1, e2) => {
2788 let mut res = e1.monomials(ev);
2789 for (m, c) in e2.monomials(ev) {
2790 let v = match res.remove(&m) {
2791 None => c,
2792 Some(v) => v + c,
2793 };
2794 res.insert(m, v);
2795 }
2796 res
2797 }
2798 Sub(e1, e2) => {
2799 let mut res = e1.monomials(ev);
2800 for (m, c) in e2.monomials(ev) {
2801 let v = match res.remove(&m) {
2802 None => -c, Some(v) => v - c,
2804 };
2805 res.insert(m, v);
2806 }
2807 res
2808 }
2809 Mul(e1, e2) => {
2810 let e1 = e1.monomials(ev);
2811 let e2 = e2.monomials(ev);
2812 mul_monomials(&e1, &e2)
2813 }
2814 Square(x) => {
2815 let x = x.monomials(ev);
2816 mul_monomials(&x, &x)
2817 }
2818 IfFeature(feature, e1, e2) => {
2819 let mut res = HashMap::new();
2820 let e1_monomials = e1.monomials(ev);
2821 let mut e2_monomials = e2.monomials(ev);
2822 for (m, c) in e1_monomials.into_iter() {
2823 let else_branch = match e2_monomials.remove(&m) {
2824 None => Expr::zero(),
2825 Some(c) => c,
2826 };
2827 let expr = Expr::IfFeature(*feature, Box::new(c), Box::new(else_branch));
2828 res.insert(m, expr);
2829 }
2830 for (m, c) in e2_monomials.into_iter() {
2831 let expr = Expr::IfFeature(*feature, Box::new(Expr::zero()), Box::new(c));
2832 res.insert(m, expr);
2833 }
2834 res
2835 }
2836 }
2837 }
2838
2839 pub fn linearize(
2866 &self,
2867 evaluated: HashSet<Column>,
2868 ) -> Result<Linearization<Expr<F, Column>, Column>, ExprError<Column>> {
2869 let mut res: HashMap<Column, Expr<F, Column>> = HashMap::new();
2870 let mut constant_term: Expr<F, Column> = Self::zero();
2871 let monomials = self.monomials(&evaluated);
2872
2873 for (m, c) in monomials {
2874 let (evaluated, mut unevaluated): (Vec<_>, _) =
2875 m.into_iter().partition(|v| evaluated.contains(&v.col));
2876 let c = evaluated
2877 .into_iter()
2878 .fold(c, |acc, v| acc * Expr::Atom(ExprInner::Cell(v)));
2879 if unevaluated.is_empty() {
2880 constant_term += c;
2881 } else if unevaluated.len() == 1 {
2882 let var = unevaluated.remove(0);
2883 match var.row {
2884 Next => {
2885 return Err(ExprError::MissingEvaluation(var.col, var.row));
2886 }
2887 Curr => {
2888 let e = match res.remove(&var.col) {
2889 Some(v) => v + c,
2890 None => c,
2891 };
2892 res.insert(var.col, e);
2893 }
2905 }
2906 } else {
2907 return Err(ExprError::FailedLinearization(unevaluated));
2908 }
2909 }
2910 Ok(Linearization {
2911 constant_term,
2912 index_terms: res.into_iter().collect(),
2913 })
2914 }
2915}
2916
2917impl<T: Literal> Zero for Operations<T>
2920where
2921 T::F: Field,
2922{
2923 fn zero() -> Self {
2924 Self::literal(T::F::zero())
2925 }
2926
2927 fn is_zero(&self) -> bool {
2928 if let Some(x) = self.to_literal_ref() {
2929 x.is_zero()
2930 } else {
2931 false
2932 }
2933 }
2934}
2935
2936impl<T: Literal + PartialEq> One for Operations<T>
2937where
2938 T::F: Field,
2939{
2940 fn one() -> Self {
2941 Self::literal(T::F::one())
2942 }
2943
2944 fn is_one(&self) -> bool {
2945 if let Some(x) = self.to_literal_ref() {
2946 x.is_one()
2947 } else {
2948 false
2949 }
2950 }
2951}
2952
2953impl<T: Literal> Neg for Operations<T>
2954where
2955 T::F: One + Neg<Output = T::F> + Copy,
2956{
2957 type Output = Self;
2958
2959 fn neg(self) -> Self {
2960 match self.to_literal() {
2961 Ok(x) => Self::literal(x.neg()),
2962 Err(x) => Operations::Mul(Box::new(Self::literal(T::F::one().neg())), Box::new(x)),
2963 }
2964 }
2965}
2966
2967impl<T: Literal> Add<Self> for Operations<T>
2968where
2969 T::F: Field,
2970{
2971 type Output = Self;
2972 fn add(self, other: Self) -> Self {
2973 if self.is_zero() {
2974 return other;
2975 }
2976 if other.is_zero() {
2977 return self;
2978 }
2979 let (x, y) = {
2980 match (self.to_literal(), other.to_literal()) {
2981 (Ok(x), Ok(y)) => return Self::literal(x + y),
2982 (Ok(x), Err(y)) => (Self::literal(x), y),
2983 (Err(x), Ok(y)) => (x, Self::literal(y)),
2984 (Err(x), Err(y)) => (x, y),
2985 }
2986 };
2987 Operations::Add(Box::new(x), Box::new(y))
2988 }
2989}
2990
2991impl<T: Literal> Sub<Self> for Operations<T>
2992where
2993 T::F: Field,
2994{
2995 type Output = Self;
2996 fn sub(self, other: Self) -> Self {
2997 if other.is_zero() {
2998 return self;
2999 }
3000 let (x, y) = {
3001 match (self.to_literal(), other.to_literal()) {
3002 (Ok(x), Ok(y)) => return Self::literal(x - y),
3003 (Ok(x), Err(y)) => (Self::literal(x), y),
3004 (Err(x), Ok(y)) => (x, Self::literal(y)),
3005 (Err(x), Err(y)) => (x, y),
3006 }
3007 };
3008 Operations::Sub(Box::new(x), Box::new(y))
3009 }
3010}
3011
3012impl<T: Literal + PartialEq> Mul<Self> for Operations<T>
3013where
3014 T::F: Field,
3015{
3016 type Output = Self;
3017 fn mul(self, other: Self) -> Self {
3018 if self.is_zero() || other.is_zero() {
3019 return Self::zero();
3020 }
3021
3022 if self.is_one() {
3023 return other;
3024 }
3025 if other.is_one() {
3026 return self;
3027 }
3028 let (x, y) = {
3029 match (self.to_literal(), other.to_literal()) {
3030 (Ok(x), Ok(y)) => return Self::literal(x * y),
3031 (Ok(x), Err(y)) => (Self::literal(x), y),
3032 (Err(x), Ok(y)) => (x, Self::literal(y)),
3033 (Err(x), Err(y)) => (x, y),
3034 }
3035 };
3036 Operations::Mul(Box::new(x), Box::new(y))
3037 }
3038}
3039
3040impl<F: Zero + Clone, Column: Clone> AddAssign<Expr<F, Column>> for Expr<F, Column>
3041where
3042 ExprInner<F, Column>: Literal,
3043 <ExprInner<F, Column> as Literal>::F: Field,
3044{
3045 fn add_assign(&mut self, other: Self) {
3046 if self.is_zero() {
3047 *self = other;
3048 } else if !other.is_zero() {
3049 *self = Expr::Add(Box::new(self.clone()), Box::new(other));
3050 }
3051 }
3052}
3053
3054impl<F, Column> MulAssign<Expr<F, Column>> for Expr<F, Column>
3055where
3056 F: Zero + One + PartialEq + Clone,
3057 Column: PartialEq + Clone,
3058 ExprInner<F, Column>: Literal,
3059 <ExprInner<F, Column> as Literal>::F: Field,
3060{
3061 fn mul_assign(&mut self, other: Self) {
3062 if self.is_zero() || other.is_zero() {
3063 *self = Self::zero();
3064 } else if self.is_one() {
3065 *self = other;
3066 } else if !other.is_one() {
3067 *self = Expr::Mul(Box::new(self.clone()), Box::new(other));
3068 }
3069 }
3070}
3071
3072impl<F: Field, Column> From<u64> for Expr<F, Column> {
3073 fn from(x: u64) -> Self {
3074 Expr::Atom(ExprInner::Constant(F::from(x)))
3075 }
3076}
3077
3078impl<'a, F: Field, Column, ChallengeTerm: AlphaChallengeTerm<'a>> From<u64>
3079 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
3080{
3081 fn from(x: u64) -> Self {
3082 ConstantTerm::Literal(F::from(x)).into()
3083 }
3084}
3085
3086impl<F: Field, ChallengeTerm> From<u64> for ConstantExpr<F, ChallengeTerm> {
3087 fn from(x: u64) -> Self {
3088 ConstantTerm::Literal(F::from(x)).into()
3089 }
3090}
3091
3092impl<'a, F: Field, Column: PartialEq + Copy, ChallengeTerm: AlphaChallengeTerm<'a>> Mul<F>
3093 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
3094{
3095 type Output = Expr<ConstantExpr<F, ChallengeTerm>, Column>;
3096
3097 fn mul(self, y: F) -> Self::Output {
3098 Expr::from(ConstantTerm::Literal(y)) * self
3099 }
3100}
3101
3102pub trait FormattedOutput: Sized {
3107 fn is_alpha(&self) -> bool;
3108 fn ocaml(&self, cache: &mut HashMap<CacheId, Self>) -> String;
3109 fn latex(&self, cache: &mut HashMap<CacheId, Self>) -> String;
3110 fn text(&self, cache: &mut HashMap<CacheId, Self>) -> String;
3111}
3112
3113impl<'a, ChallengeTerm> FormattedOutput for ChallengeTerm
3114where
3115 ChallengeTerm: AlphaChallengeTerm<'a>,
3116{
3117 fn is_alpha(&self) -> bool {
3118 self.eq(&ChallengeTerm::ALPHA)
3119 }
3120 fn ocaml(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
3121 self.to_string()
3122 }
3123
3124 fn latex(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
3125 "\\".to_string() + &self.to_string()
3126 }
3127
3128 fn text(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
3129 self.to_string()
3130 }
3131}
3132
3133impl<F: PrimeField> FormattedOutput for ConstantTerm<F> {
3134 fn is_alpha(&self) -> bool {
3135 false
3136 }
3137 fn ocaml(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
3138 use ConstantTerm::*;
3139 match self {
3140 EndoCoefficient => "endo_coefficient".to_string(),
3141 Mds { row, col } => format!("mds({row}, {col})"),
3142 Literal(x) => format!(
3143 "field(\"{:#066X}\")",
3144 Into::<num_bigint::BigUint>::into(x.into_bigint())
3145 ),
3146 }
3147 }
3148
3149 fn latex(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
3150 use ConstantTerm::*;
3151 match self {
3152 EndoCoefficient => "endo\\_coefficient".to_string(),
3153 Mds { row, col } => format!("mds({row}, {col})"),
3154 Literal(x) => format!("\\mathbb{{F}}({})", x.into_bigint().into()),
3155 }
3156 }
3157
3158 fn text(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
3159 use ConstantTerm::*;
3160 match self {
3161 EndoCoefficient => "endo_coefficient".to_string(),
3162 Mds { row, col } => format!("mds({row}, {col})"),
3163 Literal(x) => format!("0x{}", x.to_hex()),
3164 }
3165 }
3166}
3167
3168impl<'a, F: PrimeField, ChallengeTerm> FormattedOutput for ConstantExprInner<F, ChallengeTerm>
3169where
3170 ChallengeTerm: AlphaChallengeTerm<'a>,
3171{
3172 fn is_alpha(&self) -> bool {
3173 use ConstantExprInner::*;
3174 match self {
3175 Challenge(x) => x.is_alpha(),
3176 Constant(x) => x.is_alpha(),
3177 }
3178 }
3179 fn ocaml(&self, cache: &mut HashMap<CacheId, Self>) -> String {
3180 use ConstantExprInner::*;
3181 match self {
3182 Challenge(x) => {
3183 let mut inner_cache = HashMap::new();
3184 let res = x.ocaml(&mut inner_cache);
3185 inner_cache.into_iter().for_each(|(k, v)| {
3186 let _ = cache.insert(k, Challenge(v));
3187 });
3188 res
3189 }
3190 Constant(x) => {
3191 let mut inner_cache = HashMap::new();
3192 let res = x.ocaml(&mut inner_cache);
3193 inner_cache.into_iter().for_each(|(k, v)| {
3194 let _ = cache.insert(k, Constant(v));
3195 });
3196 res
3197 }
3198 }
3199 }
3200 fn latex(&self, cache: &mut HashMap<CacheId, Self>) -> String {
3201 use ConstantExprInner::*;
3202 match self {
3203 Challenge(x) => {
3204 let mut inner_cache = HashMap::new();
3205 let res = x.latex(&mut inner_cache);
3206 inner_cache.into_iter().for_each(|(k, v)| {
3207 let _ = cache.insert(k, Challenge(v));
3208 });
3209 res
3210 }
3211 Constant(x) => {
3212 let mut inner_cache = HashMap::new();
3213 let res = x.latex(&mut inner_cache);
3214 inner_cache.into_iter().for_each(|(k, v)| {
3215 let _ = cache.insert(k, Constant(v));
3216 });
3217 res
3218 }
3219 }
3220 }
3221 fn text(&self, cache: &mut HashMap<CacheId, Self>) -> String {
3222 use ConstantExprInner::*;
3223 match self {
3224 Challenge(x) => {
3225 let mut inner_cache = HashMap::new();
3226 let res = x.text(&mut inner_cache);
3227 inner_cache.into_iter().for_each(|(k, v)| {
3228 let _ = cache.insert(k, Challenge(v));
3229 });
3230 res
3231 }
3232 Constant(x) => {
3233 let mut inner_cache = HashMap::new();
3234 let res = x.text(&mut inner_cache);
3235 inner_cache.into_iter().for_each(|(k, v)| {
3236 let _ = cache.insert(k, Constant(v));
3237 });
3238 res
3239 }
3240 }
3241 }
3242}
3243
3244impl<Column: FormattedOutput + Debug> FormattedOutput for Variable<Column> {
3245 fn is_alpha(&self) -> bool {
3246 false
3247 }
3248
3249 fn ocaml(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
3250 format!("var({:?}, {:?})", self.col, self.row)
3251 }
3252
3253 fn latex(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
3254 let col = self.col.latex(&mut HashMap::new());
3255 match self.row {
3256 Curr => col,
3257 Next => format!("\\tilde{{{col}}}"),
3258 }
3259 }
3260
3261 fn text(&self, _cache: &mut HashMap<CacheId, Self>) -> String {
3262 let col = self.col.text(&mut HashMap::new());
3263 match self.row {
3264 Curr => format!("Curr({col})"),
3265 Next => format!("Next({col})"),
3266 }
3267 }
3268}
3269
3270impl<T: FormattedOutput + Clone> FormattedOutput for Operations<T> {
3271 fn is_alpha(&self) -> bool {
3272 match self {
3273 Operations::Atom(x) => x.is_alpha(),
3274 _ => false,
3275 }
3276 }
3277 fn ocaml(&self, cache: &mut HashMap<CacheId, Self>) -> String {
3278 use Operations::*;
3279 match self {
3280 Atom(x) => {
3281 let mut inner_cache = HashMap::new();
3282 let res = x.ocaml(&mut inner_cache);
3283 inner_cache.into_iter().for_each(|(k, v)| {
3284 let _ = cache.insert(k, Atom(v));
3285 });
3286 res
3287 }
3288 Pow(x, n) => {
3289 if x.is_alpha() {
3290 format!("alpha_pow({n})")
3291 } else {
3292 format!("pow({}, {n})", x.ocaml(cache))
3293 }
3294 }
3295 Add(x, y) => format!("({} + {})", x.ocaml(cache), y.ocaml(cache)),
3296 Mul(x, y) => format!("({} * {})", x.ocaml(cache), y.ocaml(cache)),
3297 Sub(x, y) => format!("({} - {})", x.ocaml(cache), y.ocaml(cache)),
3298 Double(x) => format!("double({})", x.ocaml(cache)),
3299 Square(x) => format!("square({})", x.ocaml(cache)),
3300 Cache(id, e) => {
3301 cache.insert(*id, e.as_ref().clone());
3302 id.var_name()
3303 }
3304 IfFeature(feature, e1, e2) => {
3305 format!(
3306 "if_feature({:?}, (fun () -> {}), (fun () -> {}))",
3307 feature,
3308 e1.ocaml(cache),
3309 e2.ocaml(cache)
3310 )
3311 }
3312 }
3313 }
3314
3315 fn latex(&self, cache: &mut HashMap<CacheId, Self>) -> String {
3316 use Operations::*;
3317 match self {
3318 Atom(x) => {
3319 let mut inner_cache = HashMap::new();
3320 let res = x.latex(&mut inner_cache);
3321 inner_cache.into_iter().for_each(|(k, v)| {
3322 let _ = cache.insert(k, Atom(v));
3323 });
3324 res
3325 }
3326 Pow(x, n) => format!("{}^{{{n}}}", x.latex(cache)),
3327 Add(x, y) => format!("({} + {})", x.latex(cache), y.latex(cache)),
3328 Mul(x, y) => format!("({} \\cdot {})", x.latex(cache), y.latex(cache)),
3329 Sub(x, y) => format!("({} - {})", x.latex(cache), y.latex(cache)),
3330 Double(x) => format!("2 ({})", x.latex(cache)),
3331 Square(x) => format!("({})^2", x.latex(cache)),
3332 Cache(id, e) => {
3333 cache.insert(*id, e.as_ref().clone());
3334 id.var_name()
3335 }
3336 IfFeature(feature, _, _) => format!("{feature:?}"),
3337 }
3338 }
3339
3340 fn text(&self, cache: &mut HashMap<CacheId, Self>) -> String {
3341 use Operations::*;
3342 match self {
3343 Atom(x) => {
3344 let mut inner_cache = HashMap::new();
3345 let res = x.text(&mut inner_cache);
3346 inner_cache.into_iter().for_each(|(k, v)| {
3347 let _ = cache.insert(k, Atom(v));
3348 });
3349 res
3350 }
3351 Pow(x, n) => format!("{}^{n}", x.text(cache)),
3352 Add(x, y) => format!("({} + {})", x.text(cache), y.text(cache)),
3353 Mul(x, y) => format!("({} * {})", x.text(cache), y.text(cache)),
3354 Sub(x, y) => format!("({} - {})", x.text(cache), y.text(cache)),
3355 Double(x) => format!("double({})", x.text(cache)),
3356 Square(x) => format!("square({})", x.text(cache)),
3357 Cache(id, e) => {
3358 cache.insert(*id, e.as_ref().clone());
3359 id.var_name()
3360 }
3361 IfFeature(feature, _, _) => format!("{feature:?}"),
3362 }
3363 }
3364}
3365
3366impl<'a, F, Column: FormattedOutput + Debug + Clone, ChallengeTerm> FormattedOutput
3367 for Expr<ConstantExpr<F, ChallengeTerm>, Column>
3368where
3369 F: PrimeField,
3370 ChallengeTerm: AlphaChallengeTerm<'a>,
3371{
3372 fn is_alpha(&self) -> bool {
3373 use ExprInner::*;
3374 use Operations::*;
3375 match self {
3376 Atom(Constant(x)) => x.is_alpha(),
3377 _ => false,
3378 }
3379 }
3380 fn ocaml(
3384 &self,
3385 cache: &mut HashMap<CacheId, Expr<ConstantExpr<F, ChallengeTerm>, Column>>,
3386 ) -> String {
3387 use ExprInner::*;
3388 use Operations::*;
3389 match self {
3390 Double(x) => format!("double({})", x.ocaml(cache)),
3391 Atom(Constant(x)) => {
3392 let mut inner_cache = HashMap::new();
3393 let res = x.ocaml(&mut inner_cache);
3394 inner_cache.into_iter().for_each(|(k, v)| {
3395 let _ = cache.insert(k, Atom(Constant(v)));
3396 });
3397 res
3398 }
3399 Atom(Cell(v)) => format!("cell({})", v.ocaml(&mut HashMap::new())),
3400 Atom(UnnormalizedLagrangeBasis(i)) => {
3401 format!("unnormalized_lagrange_basis({}, {})", i.zk_rows, i.offset)
3402 }
3403 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
3404 "vanishes_on_zero_knowledge_and_previous_rows".to_string()
3405 }
3406 Add(x, y) => format!("({} + {})", x.ocaml(cache), y.ocaml(cache)),
3407 Mul(x, y) => format!("({} * {})", x.ocaml(cache), y.ocaml(cache)),
3408 Sub(x, y) => format!("({} - {})", x.ocaml(cache), y.ocaml(cache)),
3409 Pow(x, d) => format!("pow({}, {d})", x.ocaml(cache)),
3410 Square(x) => format!("square({})", x.ocaml(cache)),
3411 Cache(id, e) => {
3412 cache.insert(*id, e.as_ref().clone());
3413 id.var_name()
3414 }
3415 IfFeature(feature, e1, e2) => {
3416 format!(
3417 "if_feature({:?}, (fun () -> {}), (fun () -> {}))",
3418 feature,
3419 e1.ocaml(cache),
3420 e2.ocaml(cache)
3421 )
3422 }
3423 }
3424 }
3425
3426 fn latex(
3427 &self,
3428 cache: &mut HashMap<CacheId, Expr<ConstantExpr<F, ChallengeTerm>, Column>>,
3429 ) -> String {
3430 use ExprInner::*;
3431 use Operations::*;
3432 match self {
3433 Double(x) => format!("2 ({})", x.latex(cache)),
3434 Atom(Constant(x)) => {
3435 let mut inner_cache = HashMap::new();
3436 let res = x.latex(&mut inner_cache);
3437 inner_cache.into_iter().for_each(|(k, v)| {
3438 let _ = cache.insert(k, Atom(Constant(v)));
3439 });
3440 res
3441 }
3442 Atom(Cell(v)) => v.latex(&mut HashMap::new()),
3443 Atom(UnnormalizedLagrangeBasis(RowOffset {
3444 zk_rows: true,
3445 offset: i,
3446 })) => {
3447 format!("unnormalized\\_lagrange\\_basis(zk\\_rows + {})", *i)
3448 }
3449 Atom(UnnormalizedLagrangeBasis(RowOffset {
3450 zk_rows: false,
3451 offset: i,
3452 })) => {
3453 format!("unnormalized\\_lagrange\\_basis({})", *i)
3454 }
3455 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
3456 "vanishes\\_on\\_zero\\_knowledge\\_and\\_previous\\_row".to_string()
3457 }
3458 Add(x, y) => format!("({} + {})", x.latex(cache), y.latex(cache)),
3459 Mul(x, y) => format!("({} \\cdot {})", x.latex(cache), y.latex(cache)),
3460 Sub(x, y) => format!("({} - {})", x.latex(cache), y.latex(cache)),
3461 Pow(x, d) => format!("{}^{{{d}}}", x.latex(cache)),
3462 Square(x) => format!("({})^2", x.latex(cache)),
3463 Cache(id, e) => {
3464 cache.insert(*id, e.as_ref().clone());
3465 id.latex_name()
3466 }
3467 IfFeature(feature, _, _) => format!("{feature:?}"),
3468 }
3469 }
3470
3471 fn text(
3474 &self,
3475 cache: &mut HashMap<CacheId, Expr<ConstantExpr<F, ChallengeTerm>, Column>>,
3476 ) -> String {
3477 use ExprInner::*;
3478 use Operations::*;
3479 match self {
3480 Double(x) => format!("double({})", x.text(cache)),
3481 Atom(Constant(x)) => {
3482 let mut inner_cache = HashMap::new();
3483 let res = x.text(&mut inner_cache);
3484 inner_cache.into_iter().for_each(|(k, v)| {
3485 let _ = cache.insert(k, Atom(Constant(v)));
3486 });
3487 res
3488 }
3489 Atom(Cell(v)) => v.text(&mut HashMap::new()),
3490 Atom(UnnormalizedLagrangeBasis(RowOffset {
3491 zk_rows: true,
3492 offset: i,
3493 })) => match i.cmp(&0) {
3494 Ordering::Greater => format!("unnormalized_lagrange_basis(zk_rows + {})", *i),
3495 Ordering::Equal => "unnormalized_lagrange_basis(zk_rows)".to_string(),
3496 Ordering::Less => format!("unnormalized_lagrange_basis(zk_rows - {})", (-*i)),
3497 },
3498 Atom(UnnormalizedLagrangeBasis(RowOffset {
3499 zk_rows: false,
3500 offset: i,
3501 })) => {
3502 format!("unnormalized_lagrange_basis({})", *i)
3503 }
3504 Atom(VanishesOnZeroKnowledgeAndPreviousRows) => {
3505 "vanishes_on_zero_knowledge_and_previous_rows".to_string()
3506 }
3507 Add(x, y) => format!("({} + {})", x.text(cache), y.text(cache)),
3508 Mul(x, y) => format!("({} * {})", x.text(cache), y.text(cache)),
3509 Sub(x, y) => format!("({} - {})", x.text(cache), y.text(cache)),
3510 Pow(x, d) => format!("pow({}, {d})", x.text(cache)),
3511 Square(x) => format!("square({})", x.text(cache)),
3512 Cache(id, e) => {
3513 cache.insert(*id, e.as_ref().clone());
3514 id.var_name()
3515 }
3516 IfFeature(feature, _, _) => format!("{feature:?}"),
3517 }
3518 }
3519}
3520
3521impl<'a, F, Column: FormattedOutput + Debug + Clone, ChallengeTerm>
3522 Expr<ConstantExpr<F, ChallengeTerm>, Column>
3523where
3524 F: PrimeField,
3525 ChallengeTerm: AlphaChallengeTerm<'a>,
3526{
3527 pub fn latex_str(&self) -> Vec<String> {
3530 let mut env = HashMap::new();
3531 let e = self.latex(&mut env);
3532
3533 let mut env: Vec<_> = env.into_iter().collect();
3534 env.sort_by_key(|(x, _)| *x);
3537
3538 let mut res = vec![];
3539 for (k, v) in env {
3540 let mut rhs = v.latex_str();
3541 let last = rhs.pop().expect("returned an empty expression");
3542 res.push(format!("{} = {last}", k.latex_name()));
3543 res.extend(rhs);
3544 }
3545 res.push(e);
3546 res
3547 }
3548
3549 pub fn ocaml_str(&self) -> String {
3551 let mut env = HashMap::new();
3552 let e = self.ocaml(&mut env);
3553
3554 let mut env: Vec<_> = env.into_iter().collect();
3555 env.sort_by_key(|(x, _)| *x);
3558
3559 let mut res = String::new();
3560 for (k, v) in env {
3561 let rhs = v.ocaml_str();
3562 let cached = format!("let {} = {rhs} in ", k.var_name());
3563 res.push_str(&cached);
3564 }
3565
3566 res.push_str(&e);
3567 res
3568 }
3569}
3570
3571pub mod constraints {
3577 use o1_utils::Two;
3578
3579 use crate::circuits::argument::ArgumentData;
3580 use core::fmt;
3581
3582 use super::*;
3583 use crate::circuits::berkeley_columns::{coeff, witness};
3584
3585 pub trait ExprOps<F, ChallengeTerm>:
3589 Add<Output = Self>
3590 + Sub<Output = Self>
3591 + Neg<Output = Self>
3592 + Mul<Output = Self>
3593 + AddAssign<Self>
3594 + MulAssign<Self>
3595 + Clone
3596 + Zero
3597 + One
3598 + From<u64>
3599 + fmt::Debug
3600 + fmt::Display
3601 where
3603 Self: core::marker::Sized,
3604 {
3605 fn two_pow(pow: u64) -> Self;
3607
3608 fn two_to_limb() -> Self;
3610
3611 fn two_to_2limb() -> Self;
3613
3614 fn two_to_3limb() -> Self;
3616
3617 fn double(&self) -> Self;
3619
3620 fn square(&self) -> Self;
3622
3623 fn pow(&self, p: u64) -> Self;
3625
3626 fn boolean(&self) -> Self;
3628
3629 fn crumb(&self) -> Self;
3631
3632 fn literal(x: F) -> Self;
3634
3635 fn witness(row: CurrOrNext, col: usize, env: Option<&ArgumentData<F>>) -> Self;
3637
3638 fn coeff(col: usize, env: Option<&ArgumentData<F>>) -> Self;
3640
3641 fn constant(expr: ConstantExpr<F, ChallengeTerm>, env: Option<&ArgumentData<F>>) -> Self;
3643
3644 fn cache(&self, cache: &mut Cache) -> Self;
3646 }
3647 impl<F> ExprOps<F, BerkeleyChallengeTerm>
3650 for Expr<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>
3651 where
3652 F: PrimeField,
3653 Expr<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>: core::fmt::Display,
3655 {
3656 fn two_pow(pow: u64) -> Self {
3657 Expr::<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>::literal(
3658 <F as Two<F>>::two_pow(pow),
3659 )
3660 }
3661
3662 fn two_to_limb() -> Self {
3663 Expr::<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>::literal(
3664 KimchiForeignElement::<F>::two_to_limb(),
3665 )
3666 }
3667
3668 fn two_to_2limb() -> Self {
3669 Expr::<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>::literal(
3670 KimchiForeignElement::<F>::two_to_2limb(),
3671 )
3672 }
3673
3674 fn two_to_3limb() -> Self {
3675 Expr::<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>::literal(
3676 KimchiForeignElement::<F>::two_to_3limb(),
3677 )
3678 }
3679
3680 fn double(&self) -> Self {
3681 Expr::double(self.clone())
3682 }
3683
3684 fn square(&self) -> Self {
3685 Expr::square(self.clone())
3686 }
3687
3688 fn pow(&self, p: u64) -> Self {
3689 Expr::pow(self.clone(), p)
3690 }
3691
3692 fn boolean(&self) -> Self {
3693 constraints::boolean(self)
3694 }
3695
3696 fn crumb(&self) -> Self {
3697 constraints::crumb(self)
3698 }
3699
3700 fn literal(x: F) -> Self {
3701 ConstantTerm::Literal(x).into()
3702 }
3703
3704 fn witness(row: CurrOrNext, col: usize, _: Option<&ArgumentData<F>>) -> Self {
3705 witness(col, row)
3706 }
3707
3708 fn coeff(col: usize, _: Option<&ArgumentData<F>>) -> Self {
3709 coeff(col)
3710 }
3711
3712 fn constant(
3713 expr: ConstantExpr<F, BerkeleyChallengeTerm>,
3714 _: Option<&ArgumentData<F>>,
3715 ) -> Self {
3716 Expr::from(expr)
3717 }
3718
3719 fn cache(&self, cache: &mut Cache) -> Self {
3720 Expr::Cache(cache.next_id(), Box::new(self.clone()))
3721 }
3722 }
3723 impl<F: Field> ExprOps<F, BerkeleyChallengeTerm> for F {
3726 fn two_pow(pow: u64) -> Self {
3727 <F as Two<F>>::two_pow(pow)
3728 }
3729
3730 fn two_to_limb() -> Self {
3731 KimchiForeignElement::<F>::two_to_limb()
3732 }
3733
3734 fn two_to_2limb() -> Self {
3735 KimchiForeignElement::<F>::two_to_2limb()
3736 }
3737
3738 fn two_to_3limb() -> Self {
3739 KimchiForeignElement::<F>::two_to_3limb()
3740 }
3741
3742 fn double(&self) -> Self {
3743 *self * F::from(2u64)
3744 }
3745
3746 fn square(&self) -> Self {
3747 *self * *self
3748 }
3749
3750 fn pow(&self, p: u64) -> Self {
3751 self.pow([p])
3752 }
3753
3754 fn boolean(&self) -> Self {
3755 constraints::boolean(self)
3756 }
3757
3758 fn crumb(&self) -> Self {
3759 constraints::crumb(self)
3760 }
3761
3762 fn literal(x: F) -> Self {
3763 x
3764 }
3765
3766 fn witness(row: CurrOrNext, col: usize, env: Option<&ArgumentData<F>>) -> Self {
3767 match env {
3768 Some(data) => data.witness[(row, col)],
3769 None => panic!("Missing witness"),
3770 }
3771 }
3772
3773 fn coeff(col: usize, env: Option<&ArgumentData<F>>) -> Self {
3774 match env {
3775 Some(data) => data.coeffs[col],
3776 None => panic!("Missing coefficients"),
3777 }
3778 }
3779
3780 fn constant(
3781 expr: ConstantExpr<F, BerkeleyChallengeTerm>,
3782 env: Option<&ArgumentData<F>>,
3783 ) -> Self {
3784 match env {
3785 Some(data) => expr.value(&data.constants, &data.challenges),
3786 None => panic!("Missing constants"),
3787 }
3788 }
3789
3790 fn cache(&self, _: &mut Cache) -> Self {
3791 *self
3792 }
3793 }
3794
3795 pub fn boolean<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(b: &T) -> T {
3797 b.square() - b.clone()
3798 }
3799
3800 pub fn crumb<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(x: &T) -> T {
3802 x.clone()
3804 * (x.clone() - 1u64.into())
3805 * (x.clone() - 2u64.into())
3806 * (x.clone() - 3u64.into())
3807 }
3808
3809 pub fn compact_limb<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(
3811 lo: &T,
3812 mi: &T,
3813 ) -> T {
3814 lo.clone() + mi.clone() * T::two_to_limb()
3815 }
3816}
3817
3818#[macro_export]
3821macro_rules! auto_clone {
3822 ($var:ident, $expr:expr) => {
3823 let $var = $expr;
3824 let $var = || $var.clone();
3825 };
3826 ($var:ident) => {
3827 let $var = || $var.clone();
3828 };
3829}
3830#[macro_export]
3831macro_rules! auto_clone_array {
3832 ($var:ident, $expr:expr) => {
3833 let $var = $expr;
3834 let $var = |i: usize| $var[i].clone();
3835 };
3836 ($var:ident) => {
3837 let $var = |i: usize| $var[i].clone();
3838 };
3839}
3840
3841pub use auto_clone;
3842pub use auto_clone_array;
3843
3844pub mod prologue {
3846 pub use super::{
3847 berkeley_columns::{coeff, constant, index, witness, witness_curr, witness_next, E},
3848 FeatureFlag,
3849 };
3850}