Skip to main content

kimchi/circuits/
expr.rs

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
65/// The Challenge term that contains an alpha.
66/// Is used to make a random linear combination of constraints
67pub trait AlphaChallengeTerm<'a>:
68    Copy + Clone + Debug + PartialEq + Eq + Serialize + Deserialize<'a> + Display
69{
70    const ALPHA: Self;
71}
72
73/// The collection of constants required to evaluate an `Expr`.
74#[derive(Clone)]
75pub struct Constants<F: 'static> {
76    /// The endomorphism coefficient
77    pub endo_coefficient: F,
78    /// The MDS matrix
79    pub mds: &'static [[F; 3]; 3],
80    /// The number of zero-knowledge rows
81    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    /// The generic type of column the environment can use.
92    /// In other words, with the multi-variate polynomial analogy, it is the
93    /// variables the multi-variate polynomials are defined upon.
94    /// i.e. for a polynomial `P(X, Y, Z)`, the type will represent the variable
95    /// `X`, `Y` and `Z`.
96    type Column;
97
98    /// Return the evaluation of the given column, over the domain.
99    fn get_column(&self, col: &Self::Column) -> Option<&'a Evaluations<F, D<F>>>;
100
101    /// Defines the domain over which the column is evaluated
102    fn column_domain(&self, col: &Self::Column) -> Domain;
103
104    fn get_domain(&self, d: Domain) -> D<F>;
105
106    /// Return the constants parameters that the expression might use.
107    /// For instance, it can be the matrix used by the linear layer in the
108    /// permutation.
109    fn get_constants(&self) -> &Constants<F>;
110
111    /// Return the challenges, coined by the verifier.
112    fn get_challenges(&self) -> &Challenges;
113
114    fn vanishes_on_zero_knowledge_and_previous_rows(&self) -> &'a Evaluations<F, D<F>>;
115
116    /// Return the value `prod_{j != 1} (1 - omega^j)`, used for efficiently
117    /// computing the evaluations of the unnormalized Lagrange basis polynomials.
118    fn l0_1(&self) -> F;
119}
120
121// In this file, we define...
122//
123//     The unnormalized lagrange polynomial
124//
125//         l_i(x) = (x^n - 1) / (x - omega^i) = prod_{j != i} (x - omega^j)
126//
127//     and the normalized lagrange polynomial
128//
129//         L_i(x) = l_i(x) / l_i(omega^i)
130
131/// Computes `prod_{j != n} (1 - omega^j)`
132///     Assure we don't multiply by (1 - omega^n) = (1 - omega^0) = (1 - 1) = 0
133pub 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
139// Compute the ith unnormalized lagrange basis
140pub 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)]
150/// A type representing a variable which can appear in a constraint. It specifies a column
151/// and a relative position (Curr or Next)
152pub struct Variable<Column> {
153    /// The column of this variable
154    pub col: Column,
155    /// The row (Curr of Next) of this variable
156    pub row: CurrOrNext,
157}
158
159/// Define the constant terms an expression can use.
160/// It can be any constant term (`Literal`), a matrix (`Mds` - used by the
161/// permutation used by Poseidon for instance), or endomorphism coefficients
162/// (`EndoCoefficient` - used as an optimisation).
163/// As for `challengeTerm`, it has been used initially to implement the PLONK
164/// IOP, with the custom gate Poseidon. However, the terms have no built-in
165/// semantic in the expression framework.
166/// TODO: we should generalize the expression type over challenges and constants.
167/// See <https://github.com/MinaProtocol/mina/issues/15287>
168#[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    /// Obtains the representation of some constants as a literal.
185    /// This is useful before converting Kimchi expressions with constants
186    /// to folding compatible expressions.
187    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                    // Already computed and stored this.
431                    {
432                        res.push(PolishToken::Load(*pos))
433                    }
434                    None => {
435                        // Haven't computed this yet. Compute it, then store it.
436                        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                    // True branch
445                    let tok = PolishToken::SkipIfNot(*feature, 0);
446                    res.push(tok);
447                    let len_before = res.len();
448                    /* Clone the cache, to make sure we don't try to access cached statements later
449                    when the feature flag is off. */
450                    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                    // False branch
458                    let tok = PolishToken::SkipIfNot(*feature, 0);
459                    res.push(tok);
460                    let len_before = res.len();
461                    /* Clone the cache, to make sure we don't try to access cached statements later
462                    when the feature flag is on. */
463                    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    /// Exponentiate a constant expression.
478    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    /// Evaluate the given constant expression to a field element.
491    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                // TODO: Use cache ID
507                x.value(c, chals)
508            }
509            IfFeature(_flag, _if_true, _if_false) => todo!(),
510        }
511    }
512}
513
514/// A key for a cached value
515#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)]
516pub struct CacheId(usize);
517
518/// A cache
519#[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/// The feature flags that can be used to enable or disable parts of constraints.
570#[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    /// Enabled if the table width is at least the given number
586    TableWidth(isize), // NB: isize so that we don't need to convert for OCaml :(
587    /// Enabled if the number of lookups per row is at least the given number
588    LookupsPerRow(isize), // NB: isize so that we don't need to convert for OCaml :(
589}
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(i) is
609    /// (x^n - 1) / (x - omega^i)
610    UnnormalizedLagrangeBasis(RowOffset),
611}
612
613/// An multi-variate polynomial over the base ring `C` with
614/// variables
615///
616/// - `Cell(v)` for `v : Variable`
617/// - VanishesOnZeroKnowledgeAndPreviousRows
618/// - UnnormalizedLagrangeBasis(i) for `i : i32`
619///
620/// This represents a PLONK "custom constraint", which enforces that
621/// the corresponding combination of the polynomials corresponding to
622/// the above variables should vanish on the PLONK domain.
623pub 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/// For efficiency of evaluation, we compile expressions to
816/// [reverse Polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation)
817/// expressions, which are vectors of the below tokens.
818#[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    /// Skip the given number of tokens if the feature is enabled.
833    SkipIf(FeatureFlag, usize),
834    /// Skip the given number of tokens if the feature is disabled.
835    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    /// Evaluate an RPN expression to a field element.
858    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    /// Convenience function for constructing cell variables.
943    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    /// Convenience function for constructing constant expressions.
956    pub fn constant(c: C) -> Expr<C, Column> {
957        Expr::Atom(ExprInner::Constant(c))
958    }
959
960    /// Return the degree of the expression.
961    /// The degree of a cell is defined by the first argument `d1_size`, a
962    /// constant being of degree zero. The degree of the expression is defined
963    /// recursively using the definition of the degree of a multivariate
964    /// polynomial. The function can be (and is) used to compute the domain
965    /// size, hence the name of the first argument `d1_size`.
966    /// The second parameter `zk_rows` is used to define the degree of the
967    /// constructor `VanishesOnZeroKnowledgeAndPreviousRows`.
968    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 is used to refer to evaluations that can be trivially obtained from a
1011    /// borrowed evaluation. In this case, by taking a subset of the entries
1012    /// (specifically when the borrowed `evals` is over a superset of `domain`)
1013    /// and shifting them
1014    SubEvals {
1015        domain: Domain,
1016        shift: usize,
1017        evals: &'a Evaluations<F, D<F>>,
1018    },
1019}
1020
1021/// Compute the evaluations of the unnormalized lagrange polynomial on
1022/// H_8 or H_4. Taking H_8 as an example, we show how to compute this
1023/// polynomial on the expanded domain.
1024///
1025/// Let H = < omega >, |H| = n.
1026///
1027/// Let l_i(x) be the unnormalized lagrange polynomial,
1028/// (x^n - 1) / (x - omega^i)
1029/// = prod_{j != i} (x - omega^j)
1030///
1031/// For h in H, h != omega^i,
1032/// l_i(h) = 0.
1033/// l_i(omega^i)
1034/// = prod_{j != i} (omega^i - omega^j)
1035/// = omega^{i (n - 1)} * prod_{j != i} (1 - omega^{j - i})
1036/// = omega^{i (n - 1)} * prod_{j != 0} (1 - omega^j)
1037/// = omega^{i (n - 1)} * l_0(1)
1038/// = omega^{i n} * omega^{-i} * l_0(1)
1039/// = omega^{-i} * l_0(1)
1040///
1041/// So it is easy to compute l_i(omega^i) from just l_0(1).
1042///
1043/// Also, consider the expanded domain H_8 generated by
1044/// an 8nth root of unity omega_8 (where H_8^8 = H).
1045///
1046/// Let omega_8^k in H_8. Write k = 8 * q + r with r < 8.
1047/// Then
1048/// omega_8^k = (omega_8^8)^q * omega_8^r = omega^q * omega_8^r
1049///
1050/// l_i(omega_8^k)
1051/// = (omega_8^{k n} - 1) / (omega_8^k - omega^i)
1052/// = (omega^{q n} omega_8^{r n} - 1) / (omega_8^k - omega^i)
1053/// = ((omega_8^n)^r - 1) / (omega_8^k - omega^i)
1054/// = ((omega_8^n)^r - 1) / (omega^q omega_8^r - omega^i)
1055fn 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    // Renormalize negative values to wrap around at domain size
1078    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    // Write res_domain = < omega_k > with
1090    // |res_domain| = k * |H|
1091
1092    // omega_k^0, ..., omega_k^k
1093    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            // omega_q == omega^q
1101            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    // At this point, in the 0 mod k indices, we have dummy values,
1110    // and in the other indices k*q + r, we have
1111    // 1 / (omega^q omega_k^r - omega^i)
1112
1113    // Set the 0 mod k indices
1114    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    // Finish computing the non-zero mod k indices
1120    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
1129/// Implement algebraic methods like `add`, `sub`, `mul`, `square`, etc to use
1130/// algebra on the type `EvalResult`.
1131impl<'a, F: FftField> EvalResult<'a, F> {
1132    /// Create an evaluation over the domain `res_domain`.
1133    /// The second parameter, `g`, is a function used to define the
1134    /// evaluations at a given point of the domain.
1135    /// For instance, the second parameter `g` can simply be the identity
1136    /// functions over a set of field elements.
1137    /// It can also be used to define polynomials like `x^2` when we only have the
1138    /// value of `x`. It can be used in particular to evaluate an expression (a
1139    /// multi-variate polynomial) when we only do have access to the evaluations
1140    /// of the individual variables.
1141    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    /// Call the internal function `init_` and return the computed evaluation as
1153    /// a value `Evals`.
1154    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                // TODO: Avoid the unnecessary cloning
1461                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    /// Convenience function for constructing expressions from literal
1626    /// field elements.
1627    pub fn literal(x: F) -> Self {
1628        ConstantTerm::Literal(x).into()
1629    }
1630
1631    /// Combines multiple constraints `[c0, ..., cn]` into a single constraint
1632    /// `alpha^alpha0 * c0 + alpha^{alpha0 + 1} * c1 + ... + alpha^{alpha0 + n} * cn`.
1633    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    /// Compile an expression to an RPN expression.
1644    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                    // Already computed and stored this.
1700                    {
1701                        res.push(PolishToken::Load(*pos))
1702                    }
1703                    None => {
1704                        // Haven't computed this yet. Compute it, then store it.
1705                        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                    // True branch
1714                    let tok = PolishToken::SkipIfNot(*feature, 0);
1715                    res.push(tok);
1716                    let len_before = res.len();
1717                    /* Clone the cache, to make sure we don't try to access cached statements later
1718                    when the feature flag is off. */
1719                    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                    // False branch
1727                    let tok = PolishToken::SkipIfNot(*feature, 0);
1728                    res.push(tok);
1729                    let len_before = res.len();
1730                    /* Clone the cache, to make sure we don't try to access cached statements later
1731                    when the feature flag is on. */
1732                    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        // TODO: Use cache
1753        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    /// Evaluate an expression as a field element against an environment.
1776    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    /// Evaluate an expression as a field element against the constants.
1792    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    /// Evaluate the constant expressions in this expression down into field elements.
1846    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    /// Compute the polynomial corresponding to this expression, in evaluation form.
1858    /// The routine will first replace the constants (verifier challenges and
1859    /// constants like the matrix used by `Poseidon`) in the expression with their
1860    /// respective values using `evaluate_constants` and will after evaluate the
1861    /// monomials with the corresponding column values using the method
1862    /// `evaluations`.
1863    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
1875/// Use as a result of the expression evaluations routine.
1876/// For now, the left branch is the result of an evaluation and the right branch
1877/// is the ID of an element in the cache
1878enum Either<A, B> {
1879    Left(A),
1880    Right(B),
1881}
1882
1883/// Execution mode for the experimental fused row-wise evaluator.
1884/// `KIMCHI_FUSED_EVAL=1` uses it (falling back to the vectorised path for
1885/// expressions it does not handle); `=verify` runs both and asserts they agree.
1886fn 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
1901/// Flat bytecode for the fused evaluator: a stack machine walked once per domain
1902/// row, with intermediates living in a register frame rather than full-domain
1903/// arrays.
1904enum 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
1918/// A witness-column access resolved for the fused evaluator: at row `i` it reads
1919/// `evals[(scale * i + offset) % len]` -- the same indexing as `SubEvals`.
1920struct 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    /// Evaluate an expression into a field element.
1929    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    /// Compute the polynomial corresponding to this expression, in evaluation form.
1982    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    /// Experimental: evaluate this expression over d8 with a fused, row-wise
2051    /// stack-machine bytecode -- intermediates live in registers, not full-domain
2052    /// arrays. Returns `None` for anything the spike does not handle (below d8,
2053    /// feature flags, vanishing/Lagrange atoms), so callers fall back.
2054    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        // Handle the d4 (generic + low-degree gates) and d8 (high-degree gates)
2065        // constraints. d1-degree constraints are cheap and rare -> fall back.
2066        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        // Block-at-a-time: each opcode processes `B` consecutive rows (lanes), so
2103        // the `match` dispatch is amortised over the block and the working set
2104        // (frame * B field elements) stays L1-resident. Stack slot `s` occupies
2105        // lanes `st[s*B .. s*B + B]`.
2106        const B: usize = 8;
2107        let chunk = 1usize << 14; // multiple of B
2108
2109        // 7n packing: for a d8 result every 8th row is a d1 row, where every
2110        // constraint vanishes for a satisfying witness -- so the honest prover's
2111        // value there is zero. Since the chunk and block sizes are multiples of
2112        // 8, those rows are exactly lane 0 of every block: starting the lane
2113        // loops at 1 skips them, leaving the zeros `out` was allocated with.
2114        // That is 1/8 fewer column reads and bytecode steps. (d4 keeps every
2115        // lane -- the generic constraint equals the public input on the
2116        // public-input rows.)
2117        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                    // SAFETY: the compile pass bounds every slot/lit/col index; `sp`
2131                    // never exceeds `max_depth`, `regbase + r < frame`, `l < blk <= B`,
2132                    // and the column index is reduced mod `len`.
2133                    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                                    // Materialised over `d`, indexed directly by row.
2156                                    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                                        // x^7 = (x^2)^2 * x^2 * x -- the Poseidon
2203                                        // S-box, evaluated ~4M times/proof over d8.
2204                                        // A fixed 2-square/2-mul chain avoids the
2205                                        // generic bigint exponentiation in [pow].
2206                                        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    /// Lower the expression tree into [`FOp`] bytecode (post-order). `Err` means
2246    /// an unsupported node was hit and the caller should fall back.
2247    #[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            // Feature flags are fixed for a proof: resolve at compile time and
2343            // only lower the live branch (no per-row branch).
2344            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            // The zk/previous-rows vanishing polynomial is a borrowed d8 column.
2352            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            // The unnormalized Lagrange basis is materialised over `d` (owned);
2370            // precompute it once and load it like a column.
2371            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                /* Clone the cache, to make sure we don't try to access cached statements later
2547                when the feature flag is off. */
2548                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)]
2561/// A "linearization", which is linear combination with `E` coefficients of
2562/// columns.
2563pub 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    /// Apply a function to all the coefficients in the linearization.
2579    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    /// Evaluate the constants in a linearization with `ConstantExpr<F>` coefficients down
2591    /// to literal field elements.
2592    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    /// Given a linearization and an environment, compute the polynomial corresponding to the
2608    /// linearization, in evaluation form.
2609    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    /// Given a linearization and an environment, compute the polynomial corresponding to the
2647    /// linearization, in evaluation form.
2648    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    // TODO: This function (which takes linear time)
2720    // is called repeatedly in monomials, yielding quadratic behavior for
2721    // that function. It's ok for now as we only call that function once on
2722    // a small input when producing the verification key.
2723    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                // Run the multiplication logic with square and multiply
2759                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, // Expr::constant(F::one()) * c,
2803                        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    /// There is an optimization in PLONK called "linearization" in which a certain
2840    /// polynomial is expressed as a linear combination of other polynomials in order
2841    /// to reduce the number of evaluations needed in the IOP (by relying on the homomorphic
2842    /// property of the polynomial commitments used.)
2843    ///
2844    /// The function performs this "linearization", which we now describe in some detail.
2845    ///
2846    /// In mathematical language, an expression `e: Expr<F>`
2847    /// is an element of the polynomial ring `F[V]`, where `V` is a set of variables.
2848    ///
2849    /// Given a subset `V_0` of `V` (and letting `V_1 = V \setminus V_0`), there is a map
2850    /// `factor_{V_0}: F[V] -> (F[V_1])[V_0]`. That is, polynomials with `F` coefficients in the variables `V = V_0 \cup V_1`
2851    /// are the same thing as polynomials with `F[V_1]` coefficients in variables `V_0`.
2852    ///
2853    /// There is also a function
2854    /// `lin_or_err : (F[V_1])[V_0] -> Result<Vec<(V_0, F[V_1])>, &str>`
2855    ///
2856    /// which checks if the given input is in fact a degree 1 polynomial in the variables `V_0`
2857    /// (i.e., a linear combination of `V_0` elements with `F[V_1]` coefficients)
2858    /// returning this linear combination if so.
2859    ///
2860    /// Given an expression `e` and set of columns `C_0`, letting
2861    /// `V_0 = { Variable { col: c, row: r } | c in C_0, r in { Curr, Next } }`,
2862    /// this function computes `lin_or_err(factor_{V_0}(e))`, although it does not
2863    /// compute it in that way. Instead, it computes it by reducing the expression into
2864    /// a sum of monomials with `F` coefficients, and then factors the monomials.
2865    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                        // This code used to be
2894                        //
2895                        // let v = res.entry(var.col).or_insert(0.into());
2896                        // *v = v.clone() + c
2897                        //
2898                        // but calling clone made it extremely slow, so I replaced it
2899                        // with the above that moves v out of the map with .remove and
2900                        // into v + c.
2901                        //
2902                        // I'm not sure if there's a way to do it with the HashMap API
2903                        // without calling remove.
2904                    }
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
2917// Trait implementations
2918
2919impl<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
3102//
3103// Display
3104//
3105
3106pub 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    /// Converts the expression in OCaml code
3381    /// Recursively print the expression,
3382    /// except for the cached expression that are stored in the `cache`.
3383    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    /// Recursively print the expression,
3472    /// except for the cached expression that are stored in the `cache`.
3473    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    /// Converts the expression in LaTeX
3528    // It is only used by visual tooling like kimchi-visu
3529    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        // HashMap deliberately uses an unstable order; here we sort to ensure
3535        // that the output is consistent when printing.
3536        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    /// Converts the expression in OCaml code
3550    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        // HashMap deliberately uses an unstable order; here we sort to ensure
3556        // that the output is consistent when printing.
3557        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
3571//
3572// Constraints
3573//
3574
3575/// A number of useful constraints
3576pub 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    /// This trait defines a common arithmetic operations interface
3586    /// that can be used by constraints.  It allows us to reuse
3587    /// constraint code for witness computation.
3588    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    // Add more as necessary
3602    where
3603        Self: core::marker::Sized,
3604    {
3605        /// 2^pow
3606        fn two_pow(pow: u64) -> Self;
3607
3608        /// 2^{LIMB_BITS}
3609        fn two_to_limb() -> Self;
3610
3611        /// 2^{2 * LIMB_BITS}
3612        fn two_to_2limb() -> Self;
3613
3614        /// 2^{3 * LIMB_BITS}
3615        fn two_to_3limb() -> Self;
3616
3617        /// Double the value
3618        fn double(&self) -> Self;
3619
3620        /// Compute the square of this value
3621        fn square(&self) -> Self;
3622
3623        /// Raise the value to the given power
3624        fn pow(&self, p: u64) -> Self;
3625
3626        /// Constrain to boolean
3627        fn boolean(&self) -> Self;
3628
3629        /// Constrain to crumb (i.e. two bits)
3630        fn crumb(&self) -> Self;
3631
3632        /// Create a literal
3633        fn literal(x: F) -> Self;
3634
3635        // Witness variable
3636        fn witness(row: CurrOrNext, col: usize, env: Option<&ArgumentData<F>>) -> Self;
3637
3638        /// Coefficient
3639        fn coeff(col: usize, env: Option<&ArgumentData<F>>) -> Self;
3640
3641        /// Create a constant
3642        fn constant(expr: ConstantExpr<F, ChallengeTerm>, env: Option<&ArgumentData<F>>) -> Self;
3643
3644        /// Cache item
3645        fn cache(&self, cache: &mut Cache) -> Self;
3646    }
3647    // TODO generalize with generic Column/challengeterm
3648    // We need to create a trait for berkeley_columns::Environment
3649    impl<F> ExprOps<F, BerkeleyChallengeTerm>
3650        for Expr<ConstantExpr<F, BerkeleyChallengeTerm>, berkeley_columns::Column>
3651    where
3652        F: PrimeField,
3653        // TODO remove
3654        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    // TODO generalize with generic Column/challengeterm
3724    // We need to generalize argument.rs
3725    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    /// Creates a constraint to enforce that b is either 0 or 1.
3796    pub fn boolean<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(b: &T) -> T {
3797        b.square() - b.clone()
3798    }
3799
3800    /// Crumb constraint for 2-bit value x
3801    pub fn crumb<F: Field, ChallengeTerm, T: ExprOps<F, ChallengeTerm>>(x: &T) -> T {
3802        // Assert x \in [0,3] i.e. assert x*(x - 1)*(x - 2)*(x - 3) == 0
3803        x.clone()
3804            * (x.clone() - 1u64.into())
3805            * (x.clone() - 2u64.into())
3806            * (x.clone() - 3u64.into())
3807    }
3808
3809    /// lo + mi * 2^{LIMB_BITS}
3810    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/// Auto clone macro - Helps make constraints more readable
3819/// by eliminating requirement to .clone() all the time
3820#[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
3844/// You can import this module like `use kimchi::circuits::expr::prologue::*` to obtain a number of handy aliases and helpers
3845pub mod prologue {
3846    pub use super::{
3847        berkeley_columns::{coeff, constant, index, witness, witness_curr, witness_next, E},
3848        FeatureFlag,
3849    };
3850}