triton_air/table/
processor.rs

1use std::cmp::max;
2use std::ops::Mul;
3
4use constraint_circuit::ConstraintCircuitBuilder;
5use constraint_circuit::ConstraintCircuitMonad;
6use constraint_circuit::DualRowIndicator;
7use constraint_circuit::DualRowIndicator::CurrentAux;
8use constraint_circuit::DualRowIndicator::CurrentMain;
9use constraint_circuit::DualRowIndicator::NextAux;
10use constraint_circuit::DualRowIndicator::NextMain;
11use constraint_circuit::InputIndicator;
12use constraint_circuit::SingleRowIndicator;
13use constraint_circuit::SingleRowIndicator::Aux;
14use constraint_circuit::SingleRowIndicator::Main;
15use isa::instruction::ALL_INSTRUCTIONS;
16use isa::instruction::Instruction;
17use isa::instruction::InstructionBit;
18use isa::op_stack::NUM_OP_STACK_REGISTERS;
19use isa::op_stack::NumberOfWords;
20use isa::op_stack::OpStackElement;
21use itertools::Itertools;
22use itertools::izip;
23use strum::EnumCount;
24use twenty_first::math::x_field_element::EXTENSION_DEGREE;
25use twenty_first::prelude::*;
26
27use crate::AIR;
28use crate::challenge_id::ChallengeId;
29use crate::cross_table_argument::CrossTableArg;
30use crate::cross_table_argument::EvalArg;
31use crate::cross_table_argument::LookupArg;
32use crate::cross_table_argument::PermArg;
33use crate::table;
34use crate::table_column::MasterAuxColumn;
35use crate::table_column::MasterMainColumn;
36
37/// The number of helper variable registers
38pub const NUM_HELPER_VARIABLE_REGISTERS: usize = 6;
39
40#[derive(Debug, Copy, Clone, Eq, PartialEq)]
41pub struct ProcessorTable;
42
43impl crate::private::Seal for ProcessorTable {}
44
45type MainColumn = <ProcessorTable as AIR>::MainColumn;
46type AuxColumn = <ProcessorTable as AIR>::AuxColumn;
47
48impl ProcessorTable {
49    /// # Panics
50    ///
51    /// Panics if the index is out of bounds.
52    pub fn op_stack_column_by_index(index: usize) -> MainColumn {
53        match index {
54            0 => MainColumn::ST0,
55            1 => MainColumn::ST1,
56            2 => MainColumn::ST2,
57            3 => MainColumn::ST3,
58            4 => MainColumn::ST4,
59            5 => MainColumn::ST5,
60            6 => MainColumn::ST6,
61            7 => MainColumn::ST7,
62            8 => MainColumn::ST8,
63            9 => MainColumn::ST9,
64            10 => MainColumn::ST10,
65            11 => MainColumn::ST11,
66            12 => MainColumn::ST12,
67            13 => MainColumn::ST13,
68            14 => MainColumn::ST14,
69            15 => MainColumn::ST15,
70            _ => panic!("Op Stack column index must be in [0, 15], not {index}"),
71        }
72    }
73}
74
75impl AIR for ProcessorTable {
76    type MainColumn = crate::table_column::ProcessorMainColumn;
77    type AuxColumn = crate::table_column::ProcessorAuxColumn;
78
79    fn initial_constraints(
80        circuit_builder: &ConstraintCircuitBuilder<SingleRowIndicator>,
81    ) -> Vec<ConstraintCircuitMonad<SingleRowIndicator>> {
82        let constant = |c: u32| circuit_builder.b_constant(c);
83        let x_constant = |x| circuit_builder.x_constant(x);
84        let challenge = |c| circuit_builder.challenge(c);
85        let main_row = |col: MainColumn| circuit_builder.input(Main(col.master_main_index()));
86        let aux_row = |col: AuxColumn| circuit_builder.input(Aux(col.master_aux_index()));
87
88        let clk_is_0 = main_row(MainColumn::CLK);
89        let ip_is_0 = main_row(MainColumn::IP);
90        let jsp_is_0 = main_row(MainColumn::JSP);
91        let jso_is_0 = main_row(MainColumn::JSO);
92        let jsd_is_0 = main_row(MainColumn::JSD);
93        let st0_is_0 = main_row(MainColumn::ST0);
94        let st1_is_0 = main_row(MainColumn::ST1);
95        let st2_is_0 = main_row(MainColumn::ST2);
96        let st3_is_0 = main_row(MainColumn::ST3);
97        let st4_is_0 = main_row(MainColumn::ST4);
98        let st5_is_0 = main_row(MainColumn::ST5);
99        let st6_is_0 = main_row(MainColumn::ST6);
100        let st7_is_0 = main_row(MainColumn::ST7);
101        let st8_is_0 = main_row(MainColumn::ST8);
102        let st9_is_0 = main_row(MainColumn::ST9);
103        let st10_is_0 = main_row(MainColumn::ST10);
104        let op_stack_pointer_is_16 = main_row(MainColumn::OpStackPointer) - constant(16);
105
106        // Compress the program digest using an Evaluation Argument.
107        // Lowest index in the digest corresponds to lowest index on the stack.
108        let program_digest: [_; Digest::LEN] = [
109            main_row(MainColumn::ST11),
110            main_row(MainColumn::ST12),
111            main_row(MainColumn::ST13),
112            main_row(MainColumn::ST14),
113            main_row(MainColumn::ST15),
114        ];
115        let compressed_program_digest = program_digest.into_iter().fold(
116            circuit_builder.x_constant(EvalArg::default_initial()),
117            |acc, digest_element| {
118                acc * challenge(ChallengeId::CompressProgramDigestIndeterminate) + digest_element
119            },
120        );
121        let compressed_program_digest_is_expected_program_digest =
122            compressed_program_digest - challenge(ChallengeId::CompressedProgramDigest);
123
124        // Permutation and Evaluation Arguments with all tables the Processor
125        // Table relates to
126
127        // standard input
128        let running_evaluation_for_standard_input_is_initialized_correctly =
129            aux_row(AuxColumn::InputTableEvalArg) - x_constant(EvalArg::default_initial());
130
131        // program table
132        let instruction_lookup_indeterminate =
133            challenge(ChallengeId::InstructionLookupIndeterminate);
134        let instruction_ci_weight = challenge(ChallengeId::ProgramInstructionWeight);
135        let instruction_nia_weight = challenge(ChallengeId::ProgramNextInstructionWeight);
136        let compressed_row_for_instruction_lookup = instruction_ci_weight
137            * main_row(MainColumn::CI)
138            + instruction_nia_weight * main_row(MainColumn::NIA);
139        let instruction_lookup_log_derivative_is_initialized_correctly =
140            (aux_row(AuxColumn::InstructionLookupClientLogDerivative)
141                - x_constant(LookupArg::default_initial()))
142                * (instruction_lookup_indeterminate - compressed_row_for_instruction_lookup)
143                - constant(1);
144
145        // standard output
146        let running_evaluation_for_standard_output_is_initialized_correctly =
147            aux_row(AuxColumn::OutputTableEvalArg) - x_constant(EvalArg::default_initial());
148
149        let running_product_for_op_stack_table_is_initialized_correctly =
150            aux_row(AuxColumn::OpStackTablePermArg) - x_constant(PermArg::default_initial());
151
152        // ram table
153        let running_product_for_ram_table_is_initialized_correctly =
154            aux_row(AuxColumn::RamTablePermArg) - x_constant(PermArg::default_initial());
155
156        // jump-stack table
157        let jump_stack_indeterminate = challenge(ChallengeId::JumpStackIndeterminate);
158        let jump_stack_ci_weight = challenge(ChallengeId::JumpStackCiWeight);
159        // note: `clk`, `jsp`, `jso`, and `jsd` are already constrained to be 0.
160        let compressed_row_for_jump_stack_table = jump_stack_ci_weight * main_row(MainColumn::CI);
161        let running_product_for_jump_stack_table_is_initialized_correctly =
162            aux_row(AuxColumn::JumpStackTablePermArg)
163                - x_constant(PermArg::default_initial())
164                    * (jump_stack_indeterminate - compressed_row_for_jump_stack_table);
165
166        // clock jump difference lookup argument
167        // The clock jump difference logarithmic derivative accumulator starts
168        // off having accumulated the contribution from the first row. Note that
169        // (challenge(ClockJumpDifferenceLookupIndeterminate) - main_row(CLK))
170        // collapses to challenge(ClockJumpDifferenceLookupIndeterminate)
171        // because main_row(CLK) = 0 is already a constraint.
172        let clock_jump_diff_lookup_log_derivative_is_initialized_correctly =
173            aux_row(AuxColumn::ClockJumpDifferenceLookupServerLogDerivative)
174                * challenge(ChallengeId::ClockJumpDifferenceLookupIndeterminate)
175                - main_row(MainColumn::ClockJumpDifferenceLookupMultiplicity);
176
177        // from processor to hash table
178        let hash_selector = main_row(MainColumn::CI) - constant(Instruction::Hash.opcode());
179        let hash_deselector = instruction_deselector_single_row(circuit_builder, Instruction::Hash);
180        let hash_input_indeterminate = challenge(ChallengeId::HashInputIndeterminate);
181        // the opStack is guaranteed to be initialized to 0 by virtue of other
182        // initial constraints
183        let compressed_row = constant(0);
184        let running_evaluation_hash_input_has_absorbed_first_row =
185            aux_row(AuxColumn::HashInputEvalArg)
186                - hash_input_indeterminate * x_constant(EvalArg::default_initial())
187                - compressed_row;
188        let running_evaluation_hash_input_is_default_initial =
189            aux_row(AuxColumn::HashInputEvalArg) - x_constant(EvalArg::default_initial());
190        let running_evaluation_hash_input_is_initialized_correctly = hash_selector
191            * running_evaluation_hash_input_is_default_initial
192            + hash_deselector * running_evaluation_hash_input_has_absorbed_first_row;
193
194        // from hash table to processor
195        let running_evaluation_hash_digest_is_initialized_correctly =
196            aux_row(AuxColumn::HashDigestEvalArg) - x_constant(EvalArg::default_initial());
197
198        // Hash Table – Sponge
199        let running_evaluation_sponge_absorb_is_initialized_correctly =
200            aux_row(AuxColumn::SpongeEvalArg) - x_constant(EvalArg::default_initial());
201
202        // u32 table
203        let running_sum_log_derivative_for_u32_table_is_initialized_correctly =
204            aux_row(AuxColumn::U32LookupClientLogDerivative)
205                - x_constant(LookupArg::default_initial());
206
207        vec![
208            clk_is_0,
209            ip_is_0,
210            jsp_is_0,
211            jso_is_0,
212            jsd_is_0,
213            st0_is_0,
214            st1_is_0,
215            st2_is_0,
216            st3_is_0,
217            st4_is_0,
218            st5_is_0,
219            st6_is_0,
220            st7_is_0,
221            st8_is_0,
222            st9_is_0,
223            st10_is_0,
224            compressed_program_digest_is_expected_program_digest,
225            op_stack_pointer_is_16,
226            running_evaluation_for_standard_input_is_initialized_correctly,
227            instruction_lookup_log_derivative_is_initialized_correctly,
228            running_evaluation_for_standard_output_is_initialized_correctly,
229            running_product_for_op_stack_table_is_initialized_correctly,
230            running_product_for_ram_table_is_initialized_correctly,
231            running_product_for_jump_stack_table_is_initialized_correctly,
232            clock_jump_diff_lookup_log_derivative_is_initialized_correctly,
233            running_evaluation_hash_input_is_initialized_correctly,
234            running_evaluation_hash_digest_is_initialized_correctly,
235            running_evaluation_sponge_absorb_is_initialized_correctly,
236            running_sum_log_derivative_for_u32_table_is_initialized_correctly,
237        ]
238    }
239
240    fn consistency_constraints(
241        circuit_builder: &ConstraintCircuitBuilder<SingleRowIndicator>,
242    ) -> Vec<ConstraintCircuitMonad<SingleRowIndicator>> {
243        let constant = |c: u32| circuit_builder.b_constant(c);
244        let main_row = |col: MainColumn| circuit_builder.input(Main(col.master_main_index()));
245
246        // The composition of instruction bits ib0-ib7 corresponds the current
247        // instruction ci.
248        let ib_composition = main_row(MainColumn::IB0)
249            + constant(1 << 1) * main_row(MainColumn::IB1)
250            + constant(1 << 2) * main_row(MainColumn::IB2)
251            + constant(1 << 3) * main_row(MainColumn::IB3)
252            + constant(1 << 4) * main_row(MainColumn::IB4)
253            + constant(1 << 5) * main_row(MainColumn::IB5)
254            + constant(1 << 6) * main_row(MainColumn::IB6);
255        let ci_corresponds_to_ib0_thru_ib7 = main_row(MainColumn::CI) - ib_composition;
256
257        let ib0_is_bit = main_row(MainColumn::IB0) * (main_row(MainColumn::IB0) - constant(1));
258        let ib1_is_bit = main_row(MainColumn::IB1) * (main_row(MainColumn::IB1) - constant(1));
259        let ib2_is_bit = main_row(MainColumn::IB2) * (main_row(MainColumn::IB2) - constant(1));
260        let ib3_is_bit = main_row(MainColumn::IB3) * (main_row(MainColumn::IB3) - constant(1));
261        let ib4_is_bit = main_row(MainColumn::IB4) * (main_row(MainColumn::IB4) - constant(1));
262        let ib5_is_bit = main_row(MainColumn::IB5) * (main_row(MainColumn::IB5) - constant(1));
263        let ib6_is_bit = main_row(MainColumn::IB6) * (main_row(MainColumn::IB6) - constant(1));
264        let is_padding_is_bit =
265            main_row(MainColumn::IsPadding) * (main_row(MainColumn::IsPadding) - constant(1));
266
267        // In padding rows, the clock jump difference lookup multiplicity is 0.
268        // The one row exempt from this rule is the row with CLK == 1: since the
269        // memory-like tables don't have an “awareness” of padding rows, they
270        // keep looking up clock jump differences of magnitude 1.
271        let clock_jump_diff_lookup_multiplicity_is_0_in_padding_rows =
272            main_row(MainColumn::IsPadding)
273                * (main_row(MainColumn::CLK) - constant(1))
274                * main_row(MainColumn::ClockJumpDifferenceLookupMultiplicity);
275
276        vec![
277            ib0_is_bit,
278            ib1_is_bit,
279            ib2_is_bit,
280            ib3_is_bit,
281            ib4_is_bit,
282            ib5_is_bit,
283            ib6_is_bit,
284            is_padding_is_bit,
285            ci_corresponds_to_ib0_thru_ib7,
286            clock_jump_diff_lookup_multiplicity_is_0_in_padding_rows,
287        ]
288    }
289
290    fn transition_constraints(
291        circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
292    ) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
293        let constant = |c: u64| circuit_builder.b_constant(c);
294        let curr_main_row =
295            |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
296        let next_main_row =
297            |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
298
299        // constraints common to all instructions
300        let clk_increases_by_1 =
301            next_main_row(MainColumn::CLK) - curr_main_row(MainColumn::CLK) - constant(1);
302        let is_padding_is_0_or_does_not_change = curr_main_row(MainColumn::IsPadding)
303            * (next_main_row(MainColumn::IsPadding) - curr_main_row(MainColumn::IsPadding));
304
305        let instruction_independent_constraints =
306            vec![clk_increases_by_1, is_padding_is_0_or_does_not_change];
307
308        // instruction-specific constraints
309        let transition_constraints_for_instruction =
310            |instr| transition_constraints_for_instruction(circuit_builder, instr);
311        let all_instructions_and_their_transition_constraints =
312            ALL_INSTRUCTIONS.map(|instr| (instr, transition_constraints_for_instruction(instr)));
313        let deselected_transition_constraints = combine_instruction_constraints_with_deselectors(
314            circuit_builder,
315            all_instructions_and_their_transition_constraints,
316        );
317
318        // if next row is padding row: disable transition constraints, enable
319        // padding constraints
320        let doubly_deselected_transition_constraints =
321            combine_transition_constraints_with_padding_constraints(
322                circuit_builder,
323                deselected_transition_constraints,
324            );
325
326        let table_linking_constraints = vec![
327            log_derivative_accumulates_clk_next(circuit_builder),
328            log_derivative_for_instruction_lookup_updates_correctly(circuit_builder),
329            running_product_for_jump_stack_table_updates_correctly(circuit_builder),
330            running_evaluation_hash_input_updates_correctly(circuit_builder),
331            running_evaluation_hash_digest_updates_correctly(circuit_builder),
332            running_evaluation_sponge_updates_correctly(circuit_builder),
333            log_derivative_with_u32_table_updates_correctly(circuit_builder),
334        ];
335
336        [
337            instruction_independent_constraints,
338            doubly_deselected_transition_constraints,
339            table_linking_constraints,
340        ]
341        .concat()
342    }
343
344    fn terminal_constraints(
345        circuit_builder: &ConstraintCircuitBuilder<SingleRowIndicator>,
346    ) -> Vec<ConstraintCircuitMonad<SingleRowIndicator>> {
347        let main_row = |col: MainColumn| circuit_builder.input(Main(col.master_main_index()));
348        let constant = |c| circuit_builder.b_constant(c);
349
350        let last_ci_is_halt = main_row(MainColumn::CI) - constant(Instruction::Halt.opcode_b());
351
352        vec![last_ci_is_halt]
353    }
354}
355
356/// Instruction-specific transition constraints are combined with deselectors in
357/// such a way that arbitrary sets of mutually exclusive combinations are
358/// summed, i.e.,
359///
360/// ```py
361/// [ deselector_pop * tc_pop_0 + deselector_push * tc_push_0 + ...,
362///   deselector_pop * tc_pop_1 + deselector_push * tc_push_1 + ...,
363///   ...,
364///   deselector_pop * tc_pop_i + deselector_push * tc_push_i + ...,
365///   deselector_pop * 0        + deselector_push * tc_push_{i+1} + ...,
366///   ...,
367/// ]
368/// ```
369/// For instructions that have fewer transition constraints than the maximal
370/// number of transition constraints among all instructions, the deselector is
371/// multiplied with a zero, causing no additional terms in the final sets of
372/// combined transition constraint polynomials.
373fn combine_instruction_constraints_with_deselectors(
374    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
375    instr_tc_polys_tuples: [(Instruction, Vec<ConstraintCircuitMonad<DualRowIndicator>>);
376        Instruction::COUNT],
377) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
378    let (all_instructions, all_tc_polys_for_all_instructions): (Vec<_>, Vec<_>) =
379        instr_tc_polys_tuples.into_iter().unzip();
380
381    let all_instruction_deselectors = all_instructions
382        .into_iter()
383        .map(|instr| instruction_deselector_current_row(circuit_builder, instr))
384        .collect_vec();
385
386    let max_number_of_constraints = all_tc_polys_for_all_instructions
387        .iter()
388        .map(|tc_polys_for_instr| tc_polys_for_instr.len())
389        .max()
390        .unwrap();
391
392    let zero_poly = circuit_builder.b_constant(0);
393    let all_tc_polys_for_all_instructions_transposed = (0..max_number_of_constraints)
394        .map(|idx| {
395            all_tc_polys_for_all_instructions
396                .iter()
397                .map(|tc_polys_for_instr| tc_polys_for_instr.get(idx).unwrap_or(&zero_poly))
398                .collect_vec()
399        })
400        .collect_vec();
401
402    all_tc_polys_for_all_instructions_transposed
403        .into_iter()
404        .map(|row| {
405            all_instruction_deselectors
406                .clone()
407                .into_iter()
408                .zip(row)
409                .map(|(deselector, instruction_tc)| deselector * instruction_tc.to_owned())
410                .sum()
411        })
412        .collect_vec()
413}
414
415fn combine_transition_constraints_with_padding_constraints(
416    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
417    instruction_transition_constraints: Vec<ConstraintCircuitMonad<DualRowIndicator>>,
418) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
419    let constant = |c: u64| circuit_builder.b_constant(c);
420    let curr_main_row =
421        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
422    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
423
424    let padding_row_transition_constraints = [
425        vec![
426            next_main_row(MainColumn::IP) - curr_main_row(MainColumn::IP),
427            next_main_row(MainColumn::CI) - curr_main_row(MainColumn::CI),
428            next_main_row(MainColumn::NIA) - curr_main_row(MainColumn::NIA),
429        ],
430        instruction_group_keep_jump_stack(circuit_builder),
431        instruction_group_keep_op_stack(circuit_builder),
432        instruction_group_no_ram(circuit_builder),
433        instruction_group_no_io(circuit_builder),
434    ]
435    .concat();
436
437    let padding_row_deselector = constant(1) - next_main_row(MainColumn::IsPadding);
438    let padding_row_selector = next_main_row(MainColumn::IsPadding);
439
440    let max_number_of_constraints = max(
441        instruction_transition_constraints.len(),
442        padding_row_transition_constraints.len(),
443    );
444
445    (0..max_number_of_constraints)
446        .map(|idx| {
447            let instruction_constraint = instruction_transition_constraints
448                .get(idx)
449                .unwrap_or(&constant(0))
450                .to_owned();
451            let padding_constraint = padding_row_transition_constraints
452                .get(idx)
453                .unwrap_or(&constant(0))
454                .to_owned();
455
456            instruction_constraint * padding_row_deselector.clone()
457                + padding_constraint * padding_row_selector.clone()
458        })
459        .collect_vec()
460}
461
462fn instruction_group_decompose_arg(
463    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
464) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
465    let constant = |c: u32| circuit_builder.b_constant(c);
466    let curr_main_row =
467        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
468
469    let hv0_is_a_bit =
470        curr_main_row(MainColumn::HV0) * (curr_main_row(MainColumn::HV0) - constant(1));
471    let hv1_is_a_bit =
472        curr_main_row(MainColumn::HV1) * (curr_main_row(MainColumn::HV1) - constant(1));
473    let hv2_is_a_bit =
474        curr_main_row(MainColumn::HV2) * (curr_main_row(MainColumn::HV2) - constant(1));
475    let hv3_is_a_bit =
476        curr_main_row(MainColumn::HV3) * (curr_main_row(MainColumn::HV3) - constant(1));
477
478    let helper_variables_are_binary_decomposition_of_nia = curr_main_row(MainColumn::NIA)
479        - constant(8) * curr_main_row(MainColumn::HV3)
480        - constant(4) * curr_main_row(MainColumn::HV2)
481        - constant(2) * curr_main_row(MainColumn::HV1)
482        - curr_main_row(MainColumn::HV0);
483
484    vec![
485        hv0_is_a_bit,
486        hv1_is_a_bit,
487        hv2_is_a_bit,
488        hv3_is_a_bit,
489        helper_variables_are_binary_decomposition_of_nia,
490    ]
491}
492
493/// The permutation argument accumulator with the RAM table does
494/// not change, because there is no RAM access.
495fn instruction_group_no_ram(
496    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
497) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
498    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
499    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
500
501    vec![next_aux_row(AuxColumn::RamTablePermArg) - curr_aux_row(AuxColumn::RamTablePermArg)]
502}
503
504fn instruction_group_no_io(
505    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
506) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
507    vec![
508        running_evaluation_for_standard_input_remains_unchanged(circuit_builder),
509        running_evaluation_for_standard_output_remains_unchanged(circuit_builder),
510    ]
511}
512
513/// Op Stack height does not change and except for the top n elements,
514/// the values remain also.
515fn instruction_group_op_stack_remains_except_top_n(
516    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
517    n: usize,
518) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
519    assert!(n <= NUM_OP_STACK_REGISTERS);
520
521    let curr_row = |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
522    let next_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
523
524    let stack = (0..OpStackElement::COUNT)
525        .map(ProcessorTable::op_stack_column_by_index)
526        .collect_vec();
527    let next_stack = stack.iter().map(|&st| next_row(st)).collect_vec();
528    let curr_stack = stack.iter().map(|&st| curr_row(st)).collect_vec();
529
530    let compress_stack_except_top_n = |stack: Vec<_>| -> ConstraintCircuitMonad<_> {
531        assert_eq!(NUM_OP_STACK_REGISTERS, stack.len());
532        let weight = |i| circuit_builder.challenge(stack_weight_by_index(i));
533        stack
534            .into_iter()
535            .enumerate()
536            .skip(n)
537            .map(|(i, st)| weight(i) * st)
538            .sum()
539    };
540
541    let all_but_n_top_elements_remain =
542        compress_stack_except_top_n(next_stack) - compress_stack_except_top_n(curr_stack);
543
544    let mut constraints = instruction_group_keep_op_stack_height(circuit_builder);
545    constraints.push(all_but_n_top_elements_remain);
546    constraints
547}
548
549/// Op stack does not change, _i.e._, all stack elements persist
550fn instruction_group_keep_op_stack(
551    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
552) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
553    instruction_group_op_stack_remains_except_top_n(circuit_builder, 0)
554}
555
556/// Op stack *height* does not change, _i.e._, the accumulator for the
557/// permutation argument with the op stack table remains the same as does
558/// the op stack pointer.
559fn instruction_group_keep_op_stack_height(
560    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
561) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
562    let op_stack_pointer_curr =
563        circuit_builder.input(CurrentMain(MainColumn::OpStackPointer.master_main_index()));
564    let op_stack_pointer_next =
565        circuit_builder.input(NextMain(MainColumn::OpStackPointer.master_main_index()));
566    let osp_remains_unchanged = op_stack_pointer_next - op_stack_pointer_curr;
567
568    let op_stack_table_perm_arg_curr = circuit_builder.input(CurrentAux(
569        AuxColumn::OpStackTablePermArg.master_aux_index(),
570    ));
571    let op_stack_table_perm_arg_next =
572        circuit_builder.input(NextAux(AuxColumn::OpStackTablePermArg.master_aux_index()));
573    let perm_arg_remains_unchanged = op_stack_table_perm_arg_next - op_stack_table_perm_arg_curr;
574
575    vec![osp_remains_unchanged, perm_arg_remains_unchanged]
576}
577
578fn instruction_group_grow_op_stack_and_top_two_elements_unconstrained(
579    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
580) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
581    let constant = |c: u32| circuit_builder.b_constant(c);
582    let curr_main_row =
583        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
584    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
585
586    vec![
587        next_main_row(MainColumn::ST2) - curr_main_row(MainColumn::ST1),
588        next_main_row(MainColumn::ST3) - curr_main_row(MainColumn::ST2),
589        next_main_row(MainColumn::ST4) - curr_main_row(MainColumn::ST3),
590        next_main_row(MainColumn::ST5) - curr_main_row(MainColumn::ST4),
591        next_main_row(MainColumn::ST6) - curr_main_row(MainColumn::ST5),
592        next_main_row(MainColumn::ST7) - curr_main_row(MainColumn::ST6),
593        next_main_row(MainColumn::ST8) - curr_main_row(MainColumn::ST7),
594        next_main_row(MainColumn::ST9) - curr_main_row(MainColumn::ST8),
595        next_main_row(MainColumn::ST10) - curr_main_row(MainColumn::ST9),
596        next_main_row(MainColumn::ST11) - curr_main_row(MainColumn::ST10),
597        next_main_row(MainColumn::ST12) - curr_main_row(MainColumn::ST11),
598        next_main_row(MainColumn::ST13) - curr_main_row(MainColumn::ST12),
599        next_main_row(MainColumn::ST14) - curr_main_row(MainColumn::ST13),
600        next_main_row(MainColumn::ST15) - curr_main_row(MainColumn::ST14),
601        next_main_row(MainColumn::OpStackPointer)
602            - curr_main_row(MainColumn::OpStackPointer)
603            - constant(1),
604        running_product_op_stack_accounts_for_growing_stack_by(circuit_builder, 1),
605    ]
606}
607
608fn instruction_group_grow_op_stack(
609    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
610) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
611    let curr_main_row =
612        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
613    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
614
615    let specific_constraints =
616        vec![next_main_row(MainColumn::ST1) - curr_main_row(MainColumn::ST0)];
617    let inherited_constraints =
618        instruction_group_grow_op_stack_and_top_two_elements_unconstrained(circuit_builder);
619
620    [specific_constraints, inherited_constraints].concat()
621}
622
623fn instruction_group_op_stack_shrinks_and_top_three_elements_unconstrained(
624    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
625) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
626    let constant = |c: u32| circuit_builder.b_constant(c);
627    let curr_main_row =
628        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
629    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
630
631    vec![
632        next_main_row(MainColumn::ST3) - curr_main_row(MainColumn::ST4),
633        next_main_row(MainColumn::ST4) - curr_main_row(MainColumn::ST5),
634        next_main_row(MainColumn::ST5) - curr_main_row(MainColumn::ST6),
635        next_main_row(MainColumn::ST6) - curr_main_row(MainColumn::ST7),
636        next_main_row(MainColumn::ST7) - curr_main_row(MainColumn::ST8),
637        next_main_row(MainColumn::ST8) - curr_main_row(MainColumn::ST9),
638        next_main_row(MainColumn::ST9) - curr_main_row(MainColumn::ST10),
639        next_main_row(MainColumn::ST10) - curr_main_row(MainColumn::ST11),
640        next_main_row(MainColumn::ST11) - curr_main_row(MainColumn::ST12),
641        next_main_row(MainColumn::ST12) - curr_main_row(MainColumn::ST13),
642        next_main_row(MainColumn::ST13) - curr_main_row(MainColumn::ST14),
643        next_main_row(MainColumn::ST14) - curr_main_row(MainColumn::ST15),
644        next_main_row(MainColumn::OpStackPointer) - curr_main_row(MainColumn::OpStackPointer)
645            + constant(1),
646        running_product_op_stack_accounts_for_shrinking_stack_by(circuit_builder, 1),
647    ]
648}
649
650fn instruction_group_binop(
651    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
652) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
653    let curr_main_row =
654        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
655    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
656
657    let specific_constraints = vec![
658        next_main_row(MainColumn::ST1) - curr_main_row(MainColumn::ST2),
659        next_main_row(MainColumn::ST2) - curr_main_row(MainColumn::ST3),
660    ];
661    let inherited_constraints =
662        instruction_group_op_stack_shrinks_and_top_three_elements_unconstrained(circuit_builder);
663
664    [specific_constraints, inherited_constraints].concat()
665}
666
667fn instruction_group_shrink_op_stack(
668    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
669) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
670    let curr_main_row =
671        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
672    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
673
674    let specific_constraints =
675        vec![next_main_row(MainColumn::ST0) - curr_main_row(MainColumn::ST1)];
676    let inherited_constraints = instruction_group_binop(circuit_builder);
677
678    [specific_constraints, inherited_constraints].concat()
679}
680
681fn instruction_group_keep_jump_stack(
682    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
683) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
684    let curr_main_row =
685        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
686    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
687
688    let jsp_does_not_change = next_main_row(MainColumn::JSP) - curr_main_row(MainColumn::JSP);
689    let jso_does_not_change = next_main_row(MainColumn::JSO) - curr_main_row(MainColumn::JSO);
690    let jsd_does_not_change = next_main_row(MainColumn::JSD) - curr_main_row(MainColumn::JSD);
691
692    vec![
693        jsp_does_not_change,
694        jso_does_not_change,
695        jsd_does_not_change,
696    ]
697}
698
699/// Increase the instruction pointer by 1.
700fn instruction_group_step_1(
701    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
702) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
703    let constant = |c: u32| circuit_builder.b_constant(c);
704    let curr_main_row =
705        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
706    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
707
708    let instruction_pointer_increases_by_one =
709        next_main_row(MainColumn::IP) - curr_main_row(MainColumn::IP) - constant(1);
710    [
711        instruction_group_keep_jump_stack(circuit_builder),
712        vec![instruction_pointer_increases_by_one],
713    ]
714    .concat()
715}
716
717/// Increase the instruction pointer by 2.
718fn instruction_group_step_2(
719    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
720) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
721    let constant = |c: u32| circuit_builder.b_constant(c);
722    let curr_main_row =
723        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
724    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
725
726    let instruction_pointer_increases_by_two =
727        next_main_row(MainColumn::IP) - curr_main_row(MainColumn::IP) - constant(2);
728    [
729        instruction_group_keep_jump_stack(circuit_builder),
730        vec![instruction_pointer_increases_by_two],
731    ]
732    .concat()
733}
734
735/// Internal helper function to de-duplicate functionality common between the
736/// similar (but different on a type level) functions for construction
737/// deselectors.
738fn instruction_deselector_common_functionality<II: InputIndicator>(
739    circuit_builder: &ConstraintCircuitBuilder<II>,
740    instruction: Instruction,
741    instruction_bit_polynomials: [ConstraintCircuitMonad<II>; InstructionBit::COUNT],
742) -> ConstraintCircuitMonad<II> {
743    let one = || circuit_builder.b_constant(1);
744
745    let selector_bits: [_; InstructionBit::COUNT] = [
746        instruction.ib(InstructionBit::IB0),
747        instruction.ib(InstructionBit::IB1),
748        instruction.ib(InstructionBit::IB2),
749        instruction.ib(InstructionBit::IB3),
750        instruction.ib(InstructionBit::IB4),
751        instruction.ib(InstructionBit::IB5),
752        instruction.ib(InstructionBit::IB6),
753    ];
754    let deselector_polynomials = selector_bits.map(|b| one() - circuit_builder.b_constant(b));
755
756    instruction_bit_polynomials
757        .into_iter()
758        .zip_eq(deselector_polynomials)
759        .map(|(instruction_bit_poly, deselector_poly)| instruction_bit_poly - deselector_poly)
760        .fold(one(), ConstraintCircuitMonad::mul)
761}
762
763/// A polynomial that has no solutions when `ci` is `instruction`.
764/// The number of variables in the polynomial corresponds to two rows.
765fn instruction_deselector_current_row(
766    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
767    instruction: Instruction,
768) -> ConstraintCircuitMonad<DualRowIndicator> {
769    let curr_main_row =
770        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
771
772    let instruction_bit_polynomials = [
773        curr_main_row(MainColumn::IB0),
774        curr_main_row(MainColumn::IB1),
775        curr_main_row(MainColumn::IB2),
776        curr_main_row(MainColumn::IB3),
777        curr_main_row(MainColumn::IB4),
778        curr_main_row(MainColumn::IB5),
779        curr_main_row(MainColumn::IB6),
780    ];
781
782    instruction_deselector_common_functionality(
783        circuit_builder,
784        instruction,
785        instruction_bit_polynomials,
786    )
787}
788
789/// A polynomial that has no solutions when `ci_next` is `instruction`.
790/// The number of variables in the polynomial corresponds to two rows.
791fn instruction_deselector_next_row(
792    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
793    instruction: Instruction,
794) -> ConstraintCircuitMonad<DualRowIndicator> {
795    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
796
797    let instruction_bit_polynomials = [
798        next_main_row(MainColumn::IB0),
799        next_main_row(MainColumn::IB1),
800        next_main_row(MainColumn::IB2),
801        next_main_row(MainColumn::IB3),
802        next_main_row(MainColumn::IB4),
803        next_main_row(MainColumn::IB5),
804        next_main_row(MainColumn::IB6),
805    ];
806
807    instruction_deselector_common_functionality(
808        circuit_builder,
809        instruction,
810        instruction_bit_polynomials,
811    )
812}
813
814/// A polynomial that has no solutions when `ci` is `instruction`.
815/// The number of variables in the polynomial corresponds to a single row.
816fn instruction_deselector_single_row(
817    circuit_builder: &ConstraintCircuitBuilder<SingleRowIndicator>,
818    instruction: Instruction,
819) -> ConstraintCircuitMonad<SingleRowIndicator> {
820    let main_row = |col: MainColumn| circuit_builder.input(Main(col.master_main_index()));
821
822    let instruction_bit_polynomials = [
823        main_row(MainColumn::IB0),
824        main_row(MainColumn::IB1),
825        main_row(MainColumn::IB2),
826        main_row(MainColumn::IB3),
827        main_row(MainColumn::IB4),
828        main_row(MainColumn::IB5),
829        main_row(MainColumn::IB6),
830    ];
831
832    instruction_deselector_common_functionality(
833        circuit_builder,
834        instruction,
835        instruction_bit_polynomials,
836    )
837}
838
839fn instruction_pop(
840    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
841) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
842    [
843        instruction_group_step_2(circuit_builder),
844        instruction_group_decompose_arg(circuit_builder),
845        stack_shrinks_by_any_of(circuit_builder, &NumberOfWords::legal_values()),
846        prohibit_any_illegal_number_of_words(circuit_builder),
847        instruction_group_no_ram(circuit_builder),
848        instruction_group_no_io(circuit_builder),
849    ]
850    .concat()
851}
852
853fn instruction_push(
854    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
855) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
856    let curr_main_row =
857        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
858    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
859
860    let specific_constraints =
861        vec![next_main_row(MainColumn::ST0) - curr_main_row(MainColumn::NIA)];
862    [
863        specific_constraints,
864        instruction_group_grow_op_stack(circuit_builder),
865        instruction_group_step_2(circuit_builder),
866        instruction_group_no_ram(circuit_builder),
867        instruction_group_no_io(circuit_builder),
868    ]
869    .concat()
870}
871
872fn instruction_divine(
873    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
874) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
875    [
876        instruction_group_step_2(circuit_builder),
877        instruction_group_decompose_arg(circuit_builder),
878        stack_grows_by_any_of(circuit_builder, &NumberOfWords::legal_values()),
879        prohibit_any_illegal_number_of_words(circuit_builder),
880        instruction_group_no_ram(circuit_builder),
881        instruction_group_no_io(circuit_builder),
882    ]
883    .concat()
884}
885
886fn instruction_pick(
887    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
888) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
889    let curr_row = |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
890    let next_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
891
892    let stack = (0..OpStackElement::COUNT)
893        .map(ProcessorTable::op_stack_column_by_index)
894        .collect_vec();
895    let stack_with_picked_i = |i| {
896        let mut stack = stack.clone();
897        let new_top = stack.remove(i);
898        stack.insert(0, new_top);
899        stack.into_iter()
900    };
901
902    let next_stack = stack.iter().map(|&st| next_row(st)).collect_vec();
903    let curr_stack_with_picked_i = |i| stack_with_picked_i(i).map(curr_row).collect_vec();
904
905    let compress = |stack: Vec<_>| -> ConstraintCircuitMonad<_> {
906        assert_eq!(OpStackElement::COUNT, stack.len());
907        let weight = |i| circuit_builder.challenge(stack_weight_by_index(i));
908        let enumerated_stack = stack.into_iter().enumerate();
909        enumerated_stack.map(|(i, st)| weight(i) * st).sum()
910    };
911
912    let next_stack_is_current_stack_with_picked_i = |i| {
913        indicator_polynomial(circuit_builder, i)
914            * (compress(next_stack.clone()) - compress(curr_stack_with_picked_i(i)))
915    };
916    let next_stack_is_current_stack_with_correct_element_picked = (0..OpStackElement::COUNT)
917        .map(next_stack_is_current_stack_with_picked_i)
918        .sum();
919
920    [
921        vec![next_stack_is_current_stack_with_correct_element_picked],
922        instruction_group_decompose_arg(circuit_builder),
923        instruction_group_step_2(circuit_builder),
924        instruction_group_no_ram(circuit_builder),
925        instruction_group_no_io(circuit_builder),
926        instruction_group_keep_op_stack_height(circuit_builder),
927    ]
928    .concat()
929}
930
931fn instruction_place(
932    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
933) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
934    let curr_row = |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
935    let next_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
936
937    let stack = (0..OpStackElement::COUNT)
938        .map(ProcessorTable::op_stack_column_by_index)
939        .collect_vec();
940    let stack_with_placed_i = |i| {
941        let mut stack = stack.clone();
942        let old_top = stack.remove(0);
943        stack.insert(i, old_top);
944        stack.into_iter()
945    };
946
947    let next_stack = stack.iter().map(|&st| next_row(st)).collect_vec();
948    let curr_stack_with_placed_i = |i| stack_with_placed_i(i).map(curr_row).collect_vec();
949
950    let compress = |stack: Vec<_>| -> ConstraintCircuitMonad<_> {
951        assert_eq!(OpStackElement::COUNT, stack.len());
952        let weight = |i| circuit_builder.challenge(stack_weight_by_index(i));
953        let enumerated_stack = stack.into_iter().enumerate();
954        enumerated_stack.map(|(i, st)| weight(i) * st).sum()
955    };
956
957    let next_stack_is_current_stack_with_placed_i = |i| {
958        indicator_polynomial(circuit_builder, i)
959            * (compress(next_stack.clone()) - compress(curr_stack_with_placed_i(i)))
960    };
961    let next_stack_is_current_stack_with_correct_element_placed = (0..OpStackElement::COUNT)
962        .map(next_stack_is_current_stack_with_placed_i)
963        .sum();
964
965    [
966        vec![next_stack_is_current_stack_with_correct_element_placed],
967        instruction_group_decompose_arg(circuit_builder),
968        instruction_group_step_2(circuit_builder),
969        instruction_group_no_ram(circuit_builder),
970        instruction_group_no_io(circuit_builder),
971        instruction_group_keep_op_stack_height(circuit_builder),
972    ]
973    .concat()
974}
975
976fn instruction_dup(
977    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
978) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
979    let indicator_poly = |idx| indicator_polynomial(circuit_builder, idx);
980    let curr_row = |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
981    let next_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
982
983    let st_column = ProcessorTable::op_stack_column_by_index;
984    let duplicate_element =
985        |i| indicator_poly(i) * (next_row(MainColumn::ST0) - curr_row(st_column(i)));
986    let duplicate_indicated_element = (0..OpStackElement::COUNT).map(duplicate_element).sum();
987
988    [
989        vec![duplicate_indicated_element],
990        instruction_group_decompose_arg(circuit_builder),
991        instruction_group_step_2(circuit_builder),
992        instruction_group_grow_op_stack(circuit_builder),
993        instruction_group_no_ram(circuit_builder),
994        instruction_group_no_io(circuit_builder),
995    ]
996    .concat()
997}
998
999fn instruction_swap(
1000    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1001) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1002    let curr_row = |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1003    let next_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1004
1005    let stack = (0..OpStackElement::COUNT)
1006        .map(ProcessorTable::op_stack_column_by_index)
1007        .collect_vec();
1008    let stack_with_swapped_i = |i| {
1009        let mut stack = stack.clone();
1010        stack.swap(0, i);
1011        stack.into_iter()
1012    };
1013
1014    let next_stack = stack.iter().map(|&st| next_row(st)).collect_vec();
1015    let curr_stack_with_swapped_i = |i| stack_with_swapped_i(i).map(curr_row).collect_vec();
1016    let compress = |stack: Vec<_>| -> ConstraintCircuitMonad<_> {
1017        assert_eq!(OpStackElement::COUNT, stack.len());
1018        let weight = |i| circuit_builder.challenge(stack_weight_by_index(i));
1019        let enumerated_stack = stack.into_iter().enumerate();
1020        enumerated_stack.map(|(i, st)| weight(i) * st).sum()
1021    };
1022
1023    let next_stack_is_current_stack_with_swapped_i = |i| {
1024        indicator_polynomial(circuit_builder, i)
1025            * (compress(next_stack.clone()) - compress(curr_stack_with_swapped_i(i)))
1026    };
1027    let next_stack_is_current_stack_with_correct_element_swapped = (0..OpStackElement::COUNT)
1028        .map(next_stack_is_current_stack_with_swapped_i)
1029        .sum();
1030
1031    [
1032        vec![next_stack_is_current_stack_with_correct_element_swapped],
1033        instruction_group_decompose_arg(circuit_builder),
1034        instruction_group_step_2(circuit_builder),
1035        instruction_group_no_ram(circuit_builder),
1036        instruction_group_no_io(circuit_builder),
1037        instruction_group_keep_op_stack_height(circuit_builder),
1038    ]
1039    .concat()
1040}
1041
1042fn instruction_nop(
1043    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1044) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1045    [
1046        instruction_group_step_1(circuit_builder),
1047        instruction_group_keep_op_stack(circuit_builder),
1048        instruction_group_no_ram(circuit_builder),
1049        instruction_group_no_io(circuit_builder),
1050    ]
1051    .concat()
1052}
1053
1054fn instruction_skiz(
1055    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1056) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1057    let constant = |c: u32| circuit_builder.b_constant(c);
1058    let one = || constant(1);
1059    let curr_main_row =
1060        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1061    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1062
1063    let hv0_is_inverse_of_st0 =
1064        curr_main_row(MainColumn::HV0) * curr_main_row(MainColumn::ST0) - one();
1065    let hv0_is_inverse_of_st0_or_hv0_is_0 =
1066        hv0_is_inverse_of_st0.clone() * curr_main_row(MainColumn::HV0);
1067    let hv0_is_inverse_of_st0_or_st0_is_0 = hv0_is_inverse_of_st0 * curr_main_row(MainColumn::ST0);
1068
1069    // The next instruction nia is decomposed into helper variables hv.
1070    let nia_decomposes_to_hvs = curr_main_row(MainColumn::NIA)
1071        - curr_main_row(MainColumn::HV1)
1072        - constant(1 << 1) * curr_main_row(MainColumn::HV2)
1073        - constant(1 << 3) * curr_main_row(MainColumn::HV3)
1074        - constant(1 << 5) * curr_main_row(MainColumn::HV4)
1075        - constant(1 << 7) * curr_main_row(MainColumn::HV5);
1076
1077    // If `st0` is non-zero, register `ip` is incremented by 1.
1078    // If `st0` is 0 and `nia` takes no argument, `ip` is incremented by 2.
1079    // If `st0` is 0 and `nia` takes an argument, `ip` is incremented by 3.
1080    //
1081    // The opcodes are constructed such that hv1 == 1 means that nia takes an
1082    // argument.
1083    //
1084    // Written as Disjunctive Normal Form, the constraint can be expressed as:
1085    // (Register `st0` is 0 or `ip` is incremented by 1), and
1086    // (`st0` has a multiplicative inverse or `hv1` is 1 or `ip` is incremented
1087    // by 2), and (`st0` has a multiplicative inverse or `hv1` is 0 or `ip` is
1088    // incremented by 3).
1089    let ip_case_1 = (next_main_row(MainColumn::IP) - curr_main_row(MainColumn::IP) - constant(1))
1090        * curr_main_row(MainColumn::ST0);
1091    let ip_case_2 = (next_main_row(MainColumn::IP) - curr_main_row(MainColumn::IP) - constant(2))
1092        * (curr_main_row(MainColumn::ST0) * curr_main_row(MainColumn::HV0) - one())
1093        * (curr_main_row(MainColumn::HV1) - one());
1094    let ip_case_3 = (next_main_row(MainColumn::IP) - curr_main_row(MainColumn::IP) - constant(3))
1095        * (curr_main_row(MainColumn::ST0) * curr_main_row(MainColumn::HV0) - one())
1096        * curr_main_row(MainColumn::HV1);
1097    let ip_incr_by_1_or_2_or_3 = ip_case_1 + ip_case_2 + ip_case_3;
1098
1099    let specific_constraints = vec![
1100        hv0_is_inverse_of_st0_or_hv0_is_0,
1101        hv0_is_inverse_of_st0_or_st0_is_0,
1102        nia_decomposes_to_hvs,
1103        ip_incr_by_1_or_2_or_3,
1104    ];
1105    [
1106        specific_constraints,
1107        next_instruction_range_check_constraints_for_instruction_skiz(circuit_builder),
1108        instruction_group_keep_jump_stack(circuit_builder),
1109        instruction_group_shrink_op_stack(circuit_builder),
1110        instruction_group_no_ram(circuit_builder),
1111        instruction_group_no_io(circuit_builder),
1112    ]
1113    .concat()
1114}
1115
1116fn next_instruction_range_check_constraints_for_instruction_skiz(
1117    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1118) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1119    let constant = |c: u32| circuit_builder.b_constant(c);
1120    let curr_main_row =
1121        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1122
1123    let is_0_or_1 = |var: MainColumn| curr_main_row(var) * (curr_main_row(var) - constant(1));
1124    let is_0_or_1_or_2_or_3 = |var: MainColumn| {
1125        curr_main_row(var)
1126            * (curr_main_row(var) - constant(1))
1127            * (curr_main_row(var) - constant(2))
1128            * (curr_main_row(var) - constant(3))
1129    };
1130
1131    vec![
1132        is_0_or_1(MainColumn::HV1),
1133        is_0_or_1_or_2_or_3(MainColumn::HV2),
1134        is_0_or_1_or_2_or_3(MainColumn::HV3),
1135        is_0_or_1_or_2_or_3(MainColumn::HV4),
1136        is_0_or_1_or_2_or_3(MainColumn::HV5),
1137    ]
1138}
1139
1140fn instruction_call(
1141    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1142) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1143    let constant = |c: u32| circuit_builder.b_constant(c);
1144    let curr_main_row =
1145        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1146    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1147
1148    let jsp_incr_1 = next_main_row(MainColumn::JSP) - curr_main_row(MainColumn::JSP) - constant(1);
1149    let jso_becomes_ip_plus_2 =
1150        next_main_row(MainColumn::JSO) - curr_main_row(MainColumn::IP) - constant(2);
1151    let jsd_becomes_nia = next_main_row(MainColumn::JSD) - curr_main_row(MainColumn::NIA);
1152    let ip_becomes_nia = next_main_row(MainColumn::IP) - curr_main_row(MainColumn::NIA);
1153
1154    let specific_constraints = vec![
1155        jsp_incr_1,
1156        jso_becomes_ip_plus_2,
1157        jsd_becomes_nia,
1158        ip_becomes_nia,
1159    ];
1160    [
1161        specific_constraints,
1162        instruction_group_keep_op_stack(circuit_builder),
1163        instruction_group_no_ram(circuit_builder),
1164        instruction_group_no_io(circuit_builder),
1165    ]
1166    .concat()
1167}
1168
1169fn instruction_return(
1170    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1171) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1172    let constant = |c: u32| circuit_builder.b_constant(c);
1173    let curr_main_row =
1174        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1175    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1176
1177    let jsp_decrements_by_1 =
1178        next_main_row(MainColumn::JSP) - curr_main_row(MainColumn::JSP) + constant(1);
1179    let ip_is_set_to_jso = next_main_row(MainColumn::IP) - curr_main_row(MainColumn::JSO);
1180    let specific_constraints = vec![jsp_decrements_by_1, ip_is_set_to_jso];
1181
1182    [
1183        specific_constraints,
1184        instruction_group_keep_op_stack(circuit_builder),
1185        instruction_group_no_ram(circuit_builder),
1186        instruction_group_no_io(circuit_builder),
1187    ]
1188    .concat()
1189}
1190
1191fn instruction_recurse(
1192    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1193) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1194    let curr_main_row =
1195        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1196    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1197
1198    // The instruction pointer ip is set to the last jump's destination jsd.
1199    let ip_becomes_jsd = next_main_row(MainColumn::IP) - curr_main_row(MainColumn::JSD);
1200    let specific_constraints = vec![ip_becomes_jsd];
1201    [
1202        specific_constraints,
1203        instruction_group_keep_jump_stack(circuit_builder),
1204        instruction_group_keep_op_stack(circuit_builder),
1205        instruction_group_no_ram(circuit_builder),
1206        instruction_group_no_io(circuit_builder),
1207    ]
1208    .concat()
1209}
1210
1211fn instruction_recurse_or_return(
1212    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1213) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1214    let one = || circuit_builder.b_constant(1);
1215    let curr_row = |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1216    let next_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1217
1218    // Zero if the ST5 equals ST6. One if they are not equal.
1219    let st5_eq_st6 =
1220        || curr_row(MainColumn::HV0) * (curr_row(MainColumn::ST6) - curr_row(MainColumn::ST5));
1221    let st5_neq_st6 = || one() - st5_eq_st6();
1222
1223    let maybe_return = vec![
1224        // hv0 is inverse-or-zero of the difference of ST6 and ST5.
1225        st5_neq_st6() * curr_row(MainColumn::HV0),
1226        st5_neq_st6() * (curr_row(MainColumn::ST6) - curr_row(MainColumn::ST5)),
1227        st5_neq_st6() * (next_row(MainColumn::IP) - curr_row(MainColumn::JSO)),
1228        st5_neq_st6() * (next_row(MainColumn::JSP) - curr_row(MainColumn::JSP) + one()),
1229    ];
1230    let maybe_recurse = vec![
1231        // constraints are ordered to line up nicely with group “maybe_return”
1232        st5_eq_st6() * (next_row(MainColumn::JSO) - curr_row(MainColumn::JSO)),
1233        st5_eq_st6() * (next_row(MainColumn::JSD) - curr_row(MainColumn::JSD)),
1234        st5_eq_st6() * (next_row(MainColumn::IP) - curr_row(MainColumn::JSD)),
1235        st5_eq_st6() * (next_row(MainColumn::JSP) - curr_row(MainColumn::JSP)),
1236    ];
1237
1238    // The two constraint groups are mutually exclusive: the stack element is
1239    // either equal to its successor or not, indicated by `st5_eq_st6` and
1240    // `st5_neq_st6`. Therefore, it is safe (and sound) to combine the groups
1241    // into a single set of constraints.
1242    let constraint_groups = vec![maybe_return, maybe_recurse];
1243    let specific_constraints =
1244        combine_mutually_exclusive_constraint_groups(circuit_builder, constraint_groups);
1245
1246    [
1247        specific_constraints,
1248        instruction_group_keep_op_stack(circuit_builder),
1249        instruction_group_no_ram(circuit_builder),
1250        instruction_group_no_io(circuit_builder),
1251    ]
1252    .concat()
1253}
1254
1255fn instruction_assert(
1256    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1257) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1258    let constant = |c: u32| circuit_builder.b_constant(c);
1259    let curr_main_row =
1260        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1261
1262    // The current top of the stack st0 is 1.
1263    let st_0_is_1 = curr_main_row(MainColumn::ST0) - constant(1);
1264
1265    let specific_constraints = vec![st_0_is_1];
1266    [
1267        specific_constraints,
1268        instruction_group_step_1(circuit_builder),
1269        instruction_group_shrink_op_stack(circuit_builder),
1270        instruction_group_no_ram(circuit_builder),
1271        instruction_group_no_io(circuit_builder),
1272    ]
1273    .concat()
1274}
1275
1276fn instruction_halt(
1277    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1278) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1279    let curr_main_row =
1280        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1281    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1282
1283    // The instruction executed in the following step is instruction halt.
1284    let halt_is_followed_by_halt = next_main_row(MainColumn::CI) - curr_main_row(MainColumn::CI);
1285
1286    let specific_constraints = vec![halt_is_followed_by_halt];
1287    [
1288        specific_constraints,
1289        instruction_group_step_1(circuit_builder),
1290        instruction_group_keep_op_stack(circuit_builder),
1291        instruction_group_no_ram(circuit_builder),
1292        instruction_group_no_io(circuit_builder),
1293    ]
1294    .concat()
1295}
1296
1297fn instruction_read_mem(
1298    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1299) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1300    [
1301        instruction_group_step_2(circuit_builder),
1302        instruction_group_decompose_arg(circuit_builder),
1303        read_from_ram_any_of(circuit_builder, &NumberOfWords::legal_values()),
1304        prohibit_any_illegal_number_of_words(circuit_builder),
1305        instruction_group_no_io(circuit_builder),
1306    ]
1307    .concat()
1308}
1309
1310fn instruction_write_mem(
1311    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1312) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1313    [
1314        instruction_group_step_2(circuit_builder),
1315        instruction_group_decompose_arg(circuit_builder),
1316        write_to_ram_any_of(circuit_builder, &NumberOfWords::legal_values()),
1317        prohibit_any_illegal_number_of_words(circuit_builder),
1318        instruction_group_no_io(circuit_builder),
1319    ]
1320    .concat()
1321}
1322
1323/// Two Evaluation Arguments with the Hash Table guarantee correct transition.
1324fn instruction_hash(
1325    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1326) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1327    let constant = |c: u32| circuit_builder.b_constant(c);
1328    let curr_main_row =
1329        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1330    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1331
1332    let op_stack_shrinks_by_5_and_top_5_unconstrained = vec![
1333        next_main_row(MainColumn::ST5) - curr_main_row(MainColumn::ST10),
1334        next_main_row(MainColumn::ST6) - curr_main_row(MainColumn::ST11),
1335        next_main_row(MainColumn::ST7) - curr_main_row(MainColumn::ST12),
1336        next_main_row(MainColumn::ST8) - curr_main_row(MainColumn::ST13),
1337        next_main_row(MainColumn::ST9) - curr_main_row(MainColumn::ST14),
1338        next_main_row(MainColumn::ST10) - curr_main_row(MainColumn::ST15),
1339        next_main_row(MainColumn::OpStackPointer) - curr_main_row(MainColumn::OpStackPointer)
1340            + constant(5),
1341        running_product_op_stack_accounts_for_shrinking_stack_by(circuit_builder, 5),
1342    ];
1343
1344    [
1345        instruction_group_step_1(circuit_builder),
1346        op_stack_shrinks_by_5_and_top_5_unconstrained,
1347        instruction_group_no_ram(circuit_builder),
1348        instruction_group_no_io(circuit_builder),
1349    ]
1350    .concat()
1351}
1352
1353fn instruction_merkle_step(
1354    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1355) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1356    [
1357        instruction_merkle_step_shared_constraints(circuit_builder),
1358        instruction_group_op_stack_remains_except_top_n(circuit_builder, 6),
1359        instruction_group_no_ram(circuit_builder),
1360    ]
1361    .concat()
1362}
1363
1364fn instruction_merkle_step_mem(
1365    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1366) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1367    let constant = |c: u32| circuit_builder.b_constant(c);
1368    let stack_weight = |i| circuit_builder.challenge(stack_weight_by_index(i));
1369    let curr = |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1370    let next = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1371
1372    let ram_pointers = [0, 1, 2, 3, 4].map(|i| curr(MainColumn::ST7) + constant(i));
1373    let ram_read_destinations = [
1374        MainColumn::HV0,
1375        MainColumn::HV1,
1376        MainColumn::HV2,
1377        MainColumn::HV3,
1378        MainColumn::HV4,
1379    ]
1380    .map(curr);
1381    let read_from_ram_to_hvs =
1382        read_from_ram_to(circuit_builder, ram_pointers, ram_read_destinations);
1383
1384    let st6_does_not_change = next(MainColumn::ST6) - curr(MainColumn::ST6);
1385    let st7_increments_by_5 = next(MainColumn::ST7) - curr(MainColumn::ST7) - constant(5);
1386    let st6_and_st7_update_correctly =
1387        stack_weight(6) * st6_does_not_change + stack_weight(7) * st7_increments_by_5;
1388
1389    [
1390        vec![st6_and_st7_update_correctly, read_from_ram_to_hvs],
1391        instruction_merkle_step_shared_constraints(circuit_builder),
1392        instruction_group_op_stack_remains_except_top_n(circuit_builder, 8),
1393    ]
1394    .concat()
1395}
1396
1397/// Recall that in a Merkle tree, the indices of left (respectively right)
1398/// leaves have least-significant bit 0 (respectively 1).
1399///
1400/// Two Evaluation Arguments with the Hash Table guarantee correct transition of
1401/// stack elements ST0 through ST4.
1402fn instruction_merkle_step_shared_constraints(
1403    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1404) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1405    let constant = |c: u32| circuit_builder.b_constant(c);
1406    let one = || constant(1);
1407    let curr = |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1408    let next = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1409
1410    let hv5_is_0_or_1 = curr(MainColumn::HV5) * (curr(MainColumn::HV5) - one());
1411    let new_st5_is_previous_st5_div_2 =
1412        constant(2) * next(MainColumn::ST5) + curr(MainColumn::HV5) - curr(MainColumn::ST5);
1413    let update_merkle_tree_node_index = vec![hv5_is_0_or_1, new_st5_is_previous_st5_div_2];
1414
1415    [
1416        update_merkle_tree_node_index,
1417        instruction_group_step_1(circuit_builder),
1418        instruction_group_no_io(circuit_builder),
1419    ]
1420    .concat()
1421}
1422
1423fn instruction_assert_vector(
1424    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1425) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1426    let curr_main_row =
1427        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1428
1429    let specific_constraints = vec![
1430        curr_main_row(MainColumn::ST5) - curr_main_row(MainColumn::ST0),
1431        curr_main_row(MainColumn::ST6) - curr_main_row(MainColumn::ST1),
1432        curr_main_row(MainColumn::ST7) - curr_main_row(MainColumn::ST2),
1433        curr_main_row(MainColumn::ST8) - curr_main_row(MainColumn::ST3),
1434        curr_main_row(MainColumn::ST9) - curr_main_row(MainColumn::ST4),
1435    ];
1436    [
1437        specific_constraints,
1438        instruction_group_step_1(circuit_builder),
1439        constraints_for_shrinking_stack_by(circuit_builder, 5),
1440        instruction_group_no_ram(circuit_builder),
1441        instruction_group_no_io(circuit_builder),
1442    ]
1443    .concat()
1444}
1445
1446fn instruction_sponge_init(
1447    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1448) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1449    [
1450        instruction_group_step_1(circuit_builder),
1451        instruction_group_keep_op_stack(circuit_builder),
1452        instruction_group_no_ram(circuit_builder),
1453        instruction_group_no_io(circuit_builder),
1454    ]
1455    .concat()
1456}
1457
1458fn instruction_sponge_absorb(
1459    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1460) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1461    [
1462        instruction_group_step_1(circuit_builder),
1463        constraints_for_shrinking_stack_by(circuit_builder, 10),
1464        instruction_group_no_ram(circuit_builder),
1465        instruction_group_no_io(circuit_builder),
1466    ]
1467    .concat()
1468}
1469
1470fn instruction_sponge_absorb_mem(
1471    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1472) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1473    let curr_main_row =
1474        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1475    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1476    let constant = |c| circuit_builder.b_constant(c);
1477
1478    let increment_ram_pointer = next_main_row(MainColumn::ST0)
1479        - curr_main_row(MainColumn::ST0)
1480        - constant(tip5::RATE as u32);
1481
1482    [
1483        vec![increment_ram_pointer],
1484        instruction_group_step_1(circuit_builder),
1485        instruction_group_op_stack_remains_except_top_n(circuit_builder, 5),
1486        instruction_group_no_io(circuit_builder),
1487    ]
1488    .concat()
1489}
1490
1491fn instruction_sponge_squeeze(
1492    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1493) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1494    [
1495        instruction_group_step_1(circuit_builder),
1496        constraints_for_growing_stack_by(circuit_builder, 10),
1497        instruction_group_no_ram(circuit_builder),
1498        instruction_group_no_io(circuit_builder),
1499    ]
1500    .concat()
1501}
1502
1503fn instruction_add(
1504    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1505) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1506    let curr_main_row =
1507        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1508    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1509
1510    let specific_constraints = vec![
1511        next_main_row(MainColumn::ST0)
1512            - curr_main_row(MainColumn::ST0)
1513            - curr_main_row(MainColumn::ST1),
1514    ];
1515    [
1516        specific_constraints,
1517        instruction_group_step_1(circuit_builder),
1518        instruction_group_binop(circuit_builder),
1519        instruction_group_no_ram(circuit_builder),
1520        instruction_group_no_io(circuit_builder),
1521    ]
1522    .concat()
1523}
1524
1525fn instruction_addi(
1526    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1527) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1528    let curr_main_row =
1529        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1530    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1531
1532    let specific_constraints = vec![
1533        next_main_row(MainColumn::ST0)
1534            - curr_main_row(MainColumn::ST0)
1535            - curr_main_row(MainColumn::NIA),
1536    ];
1537    [
1538        specific_constraints,
1539        instruction_group_step_2(circuit_builder),
1540        instruction_group_op_stack_remains_except_top_n(circuit_builder, 1),
1541        instruction_group_no_ram(circuit_builder),
1542        instruction_group_no_io(circuit_builder),
1543    ]
1544    .concat()
1545}
1546
1547fn instruction_mul(
1548    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1549) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1550    let curr_main_row =
1551        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1552    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1553
1554    let specific_constraints = vec![
1555        next_main_row(MainColumn::ST0)
1556            - curr_main_row(MainColumn::ST0) * curr_main_row(MainColumn::ST1),
1557    ];
1558    [
1559        specific_constraints,
1560        instruction_group_step_1(circuit_builder),
1561        instruction_group_binop(circuit_builder),
1562        instruction_group_no_ram(circuit_builder),
1563        instruction_group_no_io(circuit_builder),
1564    ]
1565    .concat()
1566}
1567
1568fn instruction_invert(
1569    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1570) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1571    let constant = |c: u32| circuit_builder.b_constant(c);
1572    let one = || constant(1);
1573    let curr_main_row =
1574        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1575    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1576
1577    let specific_constraints =
1578        vec![next_main_row(MainColumn::ST0) * curr_main_row(MainColumn::ST0) - one()];
1579    [
1580        specific_constraints,
1581        instruction_group_step_1(circuit_builder),
1582        instruction_group_op_stack_remains_except_top_n(circuit_builder, 1),
1583        instruction_group_no_ram(circuit_builder),
1584        instruction_group_no_io(circuit_builder),
1585    ]
1586    .concat()
1587}
1588
1589fn instruction_eq(
1590    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1591) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1592    let constant = |c: u32| circuit_builder.b_constant(c);
1593    let one = || constant(1);
1594    let curr_main_row =
1595        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1596    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1597
1598    let st0_eq_st1 = || {
1599        one()
1600            - curr_main_row(MainColumn::HV0)
1601                * (curr_main_row(MainColumn::ST1) - curr_main_row(MainColumn::ST0))
1602    };
1603
1604    // Helper variable hv0 is the inverse-or-zero of the difference of the
1605    // stack's two top-most elements: `hv0·(1 - hv0·(st1 - st0))`
1606    let hv0_is_inverse_of_diff_or_hv0_is_0 = curr_main_row(MainColumn::HV0) * st0_eq_st1();
1607
1608    // Helper variable hv0 is the inverse-or-zero of the difference of the
1609    // stack's two top-most elements: `(st1 - st0)·(1 - hv0·(st1 - st0))`
1610    let hv0_is_inverse_of_diff_or_diff_is_0 =
1611        (curr_main_row(MainColumn::ST1) - curr_main_row(MainColumn::ST0)) * st0_eq_st1();
1612
1613    // The new top of the stack is 1 if the difference between the stack's two
1614    // top-most elements is not invertible, 0 otherwise: `st0' - (1 - hv0·(st1 -
1615    // st0))`
1616    let st0_becomes_1_if_diff_is_not_invertible = next_main_row(MainColumn::ST0) - st0_eq_st1();
1617
1618    let specific_constraints = vec![
1619        hv0_is_inverse_of_diff_or_hv0_is_0,
1620        hv0_is_inverse_of_diff_or_diff_is_0,
1621        st0_becomes_1_if_diff_is_not_invertible,
1622    ];
1623    [
1624        specific_constraints,
1625        instruction_group_step_1(circuit_builder),
1626        instruction_group_binop(circuit_builder),
1627        instruction_group_no_ram(circuit_builder),
1628        instruction_group_no_io(circuit_builder),
1629    ]
1630    .concat()
1631}
1632
1633fn instruction_split(
1634    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1635) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1636    let constant = |c: u64| circuit_builder.b_constant(c);
1637    let one = || constant(1);
1638    let curr_main_row =
1639        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1640    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1641
1642    // The top of the stack is decomposed as 32-bit chunks into the stack's
1643    // top-most elements: st0 - (2^32·st0' + st1') = 0
1644    let st0_decomposes_to_two_32_bit_chunks = curr_main_row(MainColumn::ST0)
1645        - (constant(1 << 32) * next_main_row(MainColumn::ST1) + next_main_row(MainColumn::ST0));
1646
1647    // Helper variable hv0 is 0 if either
1648    // 1. `hv0` is the difference between 2^32 - 1 and the high 32 bits (st0'),
1649    //    or
1650    // 1. the low 32 bits (st1') are 0.
1651    //
1652    // st1'·(hv0·(st0' - (2^32 - 1)) - 1)
1653    //   lo·(hv0·(hi - 0xffff_ffff)) - 1)
1654    let hv0_holds_inverse_of_chunk_difference_or_low_bits_are_0 = {
1655        let hv0 = curr_main_row(MainColumn::HV0);
1656        let hi = next_main_row(MainColumn::ST1);
1657        let lo = next_main_row(MainColumn::ST0);
1658        let ffff_ffff = constant(0xffff_ffff);
1659
1660        lo * (hv0 * (hi - ffff_ffff) - one())
1661    };
1662
1663    let specific_constraints = vec![
1664        st0_decomposes_to_two_32_bit_chunks,
1665        hv0_holds_inverse_of_chunk_difference_or_low_bits_are_0,
1666    ];
1667    [
1668        specific_constraints,
1669        instruction_group_grow_op_stack_and_top_two_elements_unconstrained(circuit_builder),
1670        instruction_group_step_1(circuit_builder),
1671        instruction_group_no_ram(circuit_builder),
1672        instruction_group_no_io(circuit_builder),
1673    ]
1674    .concat()
1675}
1676
1677fn instruction_lt(
1678    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1679) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1680    [
1681        instruction_group_step_1(circuit_builder),
1682        instruction_group_binop(circuit_builder),
1683        instruction_group_no_ram(circuit_builder),
1684        instruction_group_no_io(circuit_builder),
1685    ]
1686    .concat()
1687}
1688
1689fn instruction_and(
1690    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1691) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1692    [
1693        instruction_group_step_1(circuit_builder),
1694        instruction_group_binop(circuit_builder),
1695        instruction_group_no_ram(circuit_builder),
1696        instruction_group_no_io(circuit_builder),
1697    ]
1698    .concat()
1699}
1700
1701fn instruction_xor(
1702    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1703) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1704    [
1705        instruction_group_step_1(circuit_builder),
1706        instruction_group_binop(circuit_builder),
1707        instruction_group_no_ram(circuit_builder),
1708        instruction_group_no_io(circuit_builder),
1709    ]
1710    .concat()
1711}
1712
1713fn instruction_log_2_floor(
1714    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1715) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1716    [
1717        instruction_group_step_1(circuit_builder),
1718        instruction_group_op_stack_remains_except_top_n(circuit_builder, 1),
1719        instruction_group_no_ram(circuit_builder),
1720        instruction_group_no_io(circuit_builder),
1721    ]
1722    .concat()
1723}
1724
1725fn instruction_pow(
1726    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1727) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1728    [
1729        instruction_group_step_1(circuit_builder),
1730        instruction_group_binop(circuit_builder),
1731        instruction_group_no_ram(circuit_builder),
1732        instruction_group_no_io(circuit_builder),
1733    ]
1734    .concat()
1735}
1736
1737fn instruction_div_mod(
1738    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1739) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1740    let curr_main_row =
1741        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1742    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1743
1744    // `n == d·q + r` means `st0 - st1·st1' - st0'`
1745    let numerator_is_quotient_times_denominator_plus_remainder = curr_main_row(MainColumn::ST0)
1746        - curr_main_row(MainColumn::ST1) * next_main_row(MainColumn::ST1)
1747        - next_main_row(MainColumn::ST0);
1748
1749    let specific_constraints = vec![numerator_is_quotient_times_denominator_plus_remainder];
1750    [
1751        specific_constraints,
1752        instruction_group_step_1(circuit_builder),
1753        instruction_group_op_stack_remains_except_top_n(circuit_builder, 2),
1754        instruction_group_no_ram(circuit_builder),
1755        instruction_group_no_io(circuit_builder),
1756    ]
1757    .concat()
1758}
1759
1760fn instruction_pop_count(
1761    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1762) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1763    [
1764        instruction_group_step_1(circuit_builder),
1765        instruction_group_op_stack_remains_except_top_n(circuit_builder, 1),
1766        instruction_group_no_ram(circuit_builder),
1767        instruction_group_no_io(circuit_builder),
1768    ]
1769    .concat()
1770}
1771
1772fn instruction_xx_add(
1773    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1774) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1775    let curr_main_row =
1776        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1777    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1778
1779    let st0_becomes_st0_plus_st3 = next_main_row(MainColumn::ST0)
1780        - curr_main_row(MainColumn::ST0)
1781        - curr_main_row(MainColumn::ST3);
1782    let st1_becomes_st1_plus_st4 = next_main_row(MainColumn::ST1)
1783        - curr_main_row(MainColumn::ST1)
1784        - curr_main_row(MainColumn::ST4);
1785    let st2_becomes_st2_plus_st5 = next_main_row(MainColumn::ST2)
1786        - curr_main_row(MainColumn::ST2)
1787        - curr_main_row(MainColumn::ST5);
1788    let specific_constraints = vec![
1789        st0_becomes_st0_plus_st3,
1790        st1_becomes_st1_plus_st4,
1791        st2_becomes_st2_plus_st5,
1792    ];
1793
1794    [
1795        specific_constraints,
1796        constraints_for_shrinking_stack_by_3_and_top_3_unconstrained(circuit_builder),
1797        instruction_group_step_1(circuit_builder),
1798        instruction_group_no_ram(circuit_builder),
1799        instruction_group_no_io(circuit_builder),
1800    ]
1801    .concat()
1802}
1803
1804fn instruction_xx_mul(
1805    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1806) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1807    let curr_main_row =
1808        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1809    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1810
1811    let [x0, x1, x2, y0, y1, y2] = [
1812        MainColumn::ST0,
1813        MainColumn::ST1,
1814        MainColumn::ST2,
1815        MainColumn::ST3,
1816        MainColumn::ST4,
1817        MainColumn::ST5,
1818    ]
1819    .map(curr_main_row);
1820    let [c0, c1, c2] = xx_product([x0, x1, x2], [y0, y1, y2]);
1821
1822    let specific_constraints = vec![
1823        next_main_row(MainColumn::ST0) - c0,
1824        next_main_row(MainColumn::ST1) - c1,
1825        next_main_row(MainColumn::ST2) - c2,
1826    ];
1827    [
1828        specific_constraints,
1829        constraints_for_shrinking_stack_by_3_and_top_3_unconstrained(circuit_builder),
1830        instruction_group_step_1(circuit_builder),
1831        instruction_group_no_ram(circuit_builder),
1832        instruction_group_no_io(circuit_builder),
1833    ]
1834    .concat()
1835}
1836
1837fn instruction_xinv(
1838    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1839) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1840    let constant = |c: u64| circuit_builder.b_constant(c);
1841    let curr_main_row =
1842        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1843    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1844
1845    let first_coefficient_of_product_of_element_and_inverse_is_1 = curr_main_row(MainColumn::ST0)
1846        * next_main_row(MainColumn::ST0)
1847        - curr_main_row(MainColumn::ST2) * next_main_row(MainColumn::ST1)
1848        - curr_main_row(MainColumn::ST1) * next_main_row(MainColumn::ST2)
1849        - constant(1);
1850
1851    let second_coefficient_of_product_of_element_and_inverse_is_0 = curr_main_row(MainColumn::ST1)
1852        * next_main_row(MainColumn::ST0)
1853        + curr_main_row(MainColumn::ST0) * next_main_row(MainColumn::ST1)
1854        - curr_main_row(MainColumn::ST2) * next_main_row(MainColumn::ST2)
1855        + curr_main_row(MainColumn::ST2) * next_main_row(MainColumn::ST1)
1856        + curr_main_row(MainColumn::ST1) * next_main_row(MainColumn::ST2);
1857
1858    let third_coefficient_of_product_of_element_and_inverse_is_0 = curr_main_row(MainColumn::ST2)
1859        * next_main_row(MainColumn::ST0)
1860        + curr_main_row(MainColumn::ST1) * next_main_row(MainColumn::ST1)
1861        + curr_main_row(MainColumn::ST0) * next_main_row(MainColumn::ST2)
1862        + curr_main_row(MainColumn::ST2) * next_main_row(MainColumn::ST2);
1863
1864    let specific_constraints = vec![
1865        first_coefficient_of_product_of_element_and_inverse_is_1,
1866        second_coefficient_of_product_of_element_and_inverse_is_0,
1867        third_coefficient_of_product_of_element_and_inverse_is_0,
1868    ];
1869    [
1870        specific_constraints,
1871        instruction_group_op_stack_remains_except_top_n(circuit_builder, 3),
1872        instruction_group_step_1(circuit_builder),
1873        instruction_group_no_ram(circuit_builder),
1874        instruction_group_no_io(circuit_builder),
1875    ]
1876    .concat()
1877}
1878
1879fn instruction_xb_mul(
1880    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1881) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1882    let curr_main_row =
1883        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1884    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
1885
1886    let [x, y0, y1, y2] = [
1887        MainColumn::ST0,
1888        MainColumn::ST1,
1889        MainColumn::ST2,
1890        MainColumn::ST3,
1891    ]
1892    .map(curr_main_row);
1893    let [c0, c1, c2] = xb_product([y0, y1, y2], x);
1894
1895    let specific_constraints = vec![
1896        next_main_row(MainColumn::ST0) - c0,
1897        next_main_row(MainColumn::ST1) - c1,
1898        next_main_row(MainColumn::ST2) - c2,
1899    ];
1900    [
1901        specific_constraints,
1902        instruction_group_op_stack_shrinks_and_top_three_elements_unconstrained(circuit_builder),
1903        instruction_group_step_1(circuit_builder),
1904        instruction_group_no_ram(circuit_builder),
1905        instruction_group_no_io(circuit_builder),
1906    ]
1907    .concat()
1908}
1909
1910fn instruction_read_io(
1911    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1912) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1913    let constraint_groups_for_legal_arguments = NumberOfWords::legal_values()
1914        .map(|n| grow_stack_by_n_and_read_n_symbols_from_input(circuit_builder, n))
1915        .to_vec();
1916    let read_any_legal_number_of_words = combine_mutually_exclusive_constraint_groups(
1917        circuit_builder,
1918        constraint_groups_for_legal_arguments,
1919    );
1920
1921    [
1922        instruction_group_step_2(circuit_builder),
1923        instruction_group_decompose_arg(circuit_builder),
1924        read_any_legal_number_of_words,
1925        prohibit_any_illegal_number_of_words(circuit_builder),
1926        instruction_group_no_ram(circuit_builder),
1927        vec![running_evaluation_for_standard_output_remains_unchanged(
1928            circuit_builder,
1929        )],
1930    ]
1931    .concat()
1932}
1933
1934fn instruction_write_io(
1935    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1936) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
1937    let constraint_groups_for_legal_arguments = NumberOfWords::legal_values()
1938        .map(|n| shrink_stack_by_n_and_write_n_symbols_to_output(circuit_builder, n))
1939        .to_vec();
1940    let write_any_of_1_through_5_elements = combine_mutually_exclusive_constraint_groups(
1941        circuit_builder,
1942        constraint_groups_for_legal_arguments,
1943    );
1944
1945    [
1946        instruction_group_step_2(circuit_builder),
1947        instruction_group_decompose_arg(circuit_builder),
1948        write_any_of_1_through_5_elements,
1949        prohibit_any_illegal_number_of_words(circuit_builder),
1950        instruction_group_no_ram(circuit_builder),
1951        vec![running_evaluation_for_standard_input_remains_unchanged(
1952            circuit_builder,
1953        )],
1954    ]
1955    .concat()
1956}
1957
1958/// Update the accumulator for the Permutation Argument with the RAM table in
1959/// accordance with reading a bunch of words from the indicated ram pointers to
1960/// the indicated destination registers.
1961///
1962/// Does not constrain the op stack by default.[^stack] For that, see:
1963/// [`read_from_ram_any_of`].
1964///
1965/// [^stack]: Op stack registers used in arguments will be constrained.
1966fn read_from_ram_to<const N: usize>(
1967    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
1968    ram_pointers: [ConstraintCircuitMonad<DualRowIndicator>; N],
1969    destinations: [ConstraintCircuitMonad<DualRowIndicator>; N],
1970) -> ConstraintCircuitMonad<DualRowIndicator> {
1971    let curr_main_row =
1972        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
1973    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
1974    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
1975    let challenge = |c: ChallengeId| circuit_builder.challenge(c);
1976    let constant = |bfe| circuit_builder.b_constant(bfe);
1977
1978    let compress_row = |(ram_pointer, destination)| {
1979        curr_main_row(MainColumn::CLK) * challenge(ChallengeId::RamClkWeight)
1980            + constant(table::ram::INSTRUCTION_TYPE_READ)
1981                * challenge(ChallengeId::RamInstructionTypeWeight)
1982            + ram_pointer * challenge(ChallengeId::RamPointerWeight)
1983            + destination * challenge(ChallengeId::RamValueWeight)
1984    };
1985
1986    let factor = ram_pointers
1987        .into_iter()
1988        .zip(destinations)
1989        .map(compress_row)
1990        .map(|compressed_row| challenge(ChallengeId::RamIndeterminate) - compressed_row)
1991        .reduce(|l, r| l * r)
1992        .unwrap_or_else(|| constant(bfe!(1)));
1993    curr_aux_row(AuxColumn::RamTablePermArg) * factor - next_aux_row(AuxColumn::RamTablePermArg)
1994}
1995
1996fn xx_product<Indicator: InputIndicator>(
1997    [x_0, x_1, x_2]: [ConstraintCircuitMonad<Indicator>; EXTENSION_DEGREE],
1998    [y_0, y_1, y_2]: [ConstraintCircuitMonad<Indicator>; EXTENSION_DEGREE],
1999) -> [ConstraintCircuitMonad<Indicator>; EXTENSION_DEGREE] {
2000    let z0 = x_0.clone() * y_0.clone();
2001    let z1 = x_1.clone() * y_0.clone() + x_0.clone() * y_1.clone();
2002    let z2 = x_2.clone() * y_0 + x_1.clone() * y_1.clone() + x_0 * y_2.clone();
2003    let z3 = x_2.clone() * y_1 + x_1 * y_2.clone();
2004    let z4 = x_2 * y_2;
2005
2006    // reduce modulo x³ - x + 1
2007    [z0 - z3.clone(), z1 - z4.clone() + z3, z2 + z4]
2008}
2009
2010fn xb_product<Indicator: InputIndicator>(
2011    [x_0, x_1, x_2]: [ConstraintCircuitMonad<Indicator>; EXTENSION_DEGREE],
2012    y: ConstraintCircuitMonad<Indicator>,
2013) -> [ConstraintCircuitMonad<Indicator>; EXTENSION_DEGREE] {
2014    let z0 = x_0 * y.clone();
2015    let z1 = x_1 * y.clone();
2016    let z2 = x_2 * y;
2017    [z0, z1, z2]
2018}
2019
2020fn update_dotstep_accumulator(
2021    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2022    accumulator_indices: [MainColumn; EXTENSION_DEGREE],
2023    difference: [ConstraintCircuitMonad<DualRowIndicator>; EXTENSION_DEGREE],
2024) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2025    let curr_main_row =
2026        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2027    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2028    let curr = accumulator_indices.map(curr_main_row);
2029    let next = accumulator_indices.map(next_main_row);
2030    izip!(curr, next, difference)
2031        .map(|(c, n, d)| n - c - d)
2032        .collect()
2033}
2034
2035fn instruction_xx_dot_step(
2036    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2037) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2038    let curr_main_row =
2039        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2040    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2041    let constant = |c| circuit_builder.b_constant(c);
2042
2043    let increment_ram_pointer_st0 =
2044        next_main_row(MainColumn::ST0) - curr_main_row(MainColumn::ST0) - constant(3);
2045    let increment_ram_pointer_st1 =
2046        next_main_row(MainColumn::ST1) - curr_main_row(MainColumn::ST1) - constant(3);
2047
2048    let rhs_ptr0 = curr_main_row(MainColumn::ST0);
2049    let rhs_ptr1 = rhs_ptr0.clone() + constant(1);
2050    let rhs_ptr2 = rhs_ptr0.clone() + constant(2);
2051    let lhs_ptr0 = curr_main_row(MainColumn::ST1);
2052    let lhs_ptr1 = lhs_ptr0.clone() + constant(1);
2053    let lhs_ptr2 = lhs_ptr0.clone() + constant(2);
2054    let ram_read_sources = [rhs_ptr0, rhs_ptr1, rhs_ptr2, lhs_ptr0, lhs_ptr1, lhs_ptr2];
2055    let ram_read_destinations = [
2056        MainColumn::HV0,
2057        MainColumn::HV1,
2058        MainColumn::HV2,
2059        MainColumn::HV3,
2060        MainColumn::HV4,
2061        MainColumn::HV5,
2062    ]
2063    .map(curr_main_row);
2064    let read_two_xfes_from_ram =
2065        read_from_ram_to(circuit_builder, ram_read_sources, ram_read_destinations);
2066
2067    let ram_pointer_constraints = vec![
2068        increment_ram_pointer_st0,
2069        increment_ram_pointer_st1,
2070        read_two_xfes_from_ram,
2071    ];
2072
2073    let [hv0, hv1, hv2, hv3, hv4, hv5] = [
2074        MainColumn::HV0,
2075        MainColumn::HV1,
2076        MainColumn::HV2,
2077        MainColumn::HV3,
2078        MainColumn::HV4,
2079        MainColumn::HV5,
2080    ]
2081    .map(curr_main_row);
2082    let hv_product = xx_product([hv0, hv1, hv2], [hv3, hv4, hv5]);
2083
2084    [
2085        ram_pointer_constraints,
2086        update_dotstep_accumulator(
2087            circuit_builder,
2088            [MainColumn::ST2, MainColumn::ST3, MainColumn::ST4],
2089            hv_product,
2090        ),
2091        instruction_group_step_1(circuit_builder),
2092        instruction_group_no_io(circuit_builder),
2093        instruction_group_op_stack_remains_except_top_n(circuit_builder, 5),
2094    ]
2095    .concat()
2096}
2097
2098fn instruction_xb_dot_step(
2099    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2100) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2101    let curr_main_row =
2102        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2103    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2104    let constant = |c| circuit_builder.b_constant(c);
2105
2106    let increment_ram_pointer_st0 =
2107        next_main_row(MainColumn::ST0) - curr_main_row(MainColumn::ST0) - constant(1);
2108    let increment_ram_pointer_st1 =
2109        next_main_row(MainColumn::ST1) - curr_main_row(MainColumn::ST1) - constant(3);
2110
2111    let rhs_ptr0 = curr_main_row(MainColumn::ST0);
2112    let lhs_ptr0 = curr_main_row(MainColumn::ST1);
2113    let lhs_ptr1 = lhs_ptr0.clone() + constant(1);
2114    let lhs_ptr2 = lhs_ptr0.clone() + constant(2);
2115    let ram_read_sources = [rhs_ptr0, lhs_ptr0, lhs_ptr1, lhs_ptr2];
2116    let ram_read_destinations = [
2117        MainColumn::HV0,
2118        MainColumn::HV1,
2119        MainColumn::HV2,
2120        MainColumn::HV3,
2121    ]
2122    .map(curr_main_row);
2123    let read_bfe_and_xfe_from_ram =
2124        read_from_ram_to(circuit_builder, ram_read_sources, ram_read_destinations);
2125
2126    let ram_pointer_constraints = vec![
2127        increment_ram_pointer_st0,
2128        increment_ram_pointer_st1,
2129        read_bfe_and_xfe_from_ram,
2130    ];
2131
2132    let [hv0, hv1, hv2, hv3] = [
2133        MainColumn::HV0,
2134        MainColumn::HV1,
2135        MainColumn::HV2,
2136        MainColumn::HV3,
2137    ]
2138    .map(curr_main_row);
2139    let hv_product = xb_product([hv1, hv2, hv3], hv0);
2140
2141    [
2142        ram_pointer_constraints,
2143        update_dotstep_accumulator(
2144            circuit_builder,
2145            [MainColumn::ST2, MainColumn::ST3, MainColumn::ST4],
2146            hv_product,
2147        ),
2148        instruction_group_step_1(circuit_builder),
2149        instruction_group_no_io(circuit_builder),
2150        instruction_group_op_stack_remains_except_top_n(circuit_builder, 5),
2151    ]
2152    .concat()
2153}
2154
2155#[doc(hidden)] // allows testing in different crate
2156pub fn transition_constraints_for_instruction(
2157    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2158    instruction: Instruction,
2159) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2160    match instruction {
2161        Instruction::Pop(_) => instruction_pop(circuit_builder),
2162        Instruction::Push(_) => instruction_push(circuit_builder),
2163        Instruction::Divine(_) => instruction_divine(circuit_builder),
2164        Instruction::Pick(_) => instruction_pick(circuit_builder),
2165        Instruction::Place(_) => instruction_place(circuit_builder),
2166        Instruction::Dup(_) => instruction_dup(circuit_builder),
2167        Instruction::Swap(_) => instruction_swap(circuit_builder),
2168        Instruction::Halt => instruction_halt(circuit_builder),
2169        Instruction::Nop => instruction_nop(circuit_builder),
2170        Instruction::Skiz => instruction_skiz(circuit_builder),
2171        Instruction::Call(_) => instruction_call(circuit_builder),
2172        Instruction::Return => instruction_return(circuit_builder),
2173        Instruction::Recurse => instruction_recurse(circuit_builder),
2174        Instruction::RecurseOrReturn => instruction_recurse_or_return(circuit_builder),
2175        Instruction::Assert => instruction_assert(circuit_builder),
2176        Instruction::ReadMem(_) => instruction_read_mem(circuit_builder),
2177        Instruction::WriteMem(_) => instruction_write_mem(circuit_builder),
2178        Instruction::Hash => instruction_hash(circuit_builder),
2179        Instruction::AssertVector => instruction_assert_vector(circuit_builder),
2180        Instruction::SpongeInit => instruction_sponge_init(circuit_builder),
2181        Instruction::SpongeAbsorb => instruction_sponge_absorb(circuit_builder),
2182        Instruction::SpongeAbsorbMem => instruction_sponge_absorb_mem(circuit_builder),
2183        Instruction::SpongeSqueeze => instruction_sponge_squeeze(circuit_builder),
2184        Instruction::Add => instruction_add(circuit_builder),
2185        Instruction::AddI(_) => instruction_addi(circuit_builder),
2186        Instruction::Mul => instruction_mul(circuit_builder),
2187        Instruction::Invert => instruction_invert(circuit_builder),
2188        Instruction::Eq => instruction_eq(circuit_builder),
2189        Instruction::Split => instruction_split(circuit_builder),
2190        Instruction::Lt => instruction_lt(circuit_builder),
2191        Instruction::And => instruction_and(circuit_builder),
2192        Instruction::Xor => instruction_xor(circuit_builder),
2193        Instruction::Log2Floor => instruction_log_2_floor(circuit_builder),
2194        Instruction::Pow => instruction_pow(circuit_builder),
2195        Instruction::DivMod => instruction_div_mod(circuit_builder),
2196        Instruction::PopCount => instruction_pop_count(circuit_builder),
2197        Instruction::XxAdd => instruction_xx_add(circuit_builder),
2198        Instruction::XxMul => instruction_xx_mul(circuit_builder),
2199        Instruction::XInvert => instruction_xinv(circuit_builder),
2200        Instruction::XbMul => instruction_xb_mul(circuit_builder),
2201        Instruction::ReadIo(_) => instruction_read_io(circuit_builder),
2202        Instruction::WriteIo(_) => instruction_write_io(circuit_builder),
2203        Instruction::MerkleStep => instruction_merkle_step(circuit_builder),
2204        Instruction::MerkleStepMem => instruction_merkle_step_mem(circuit_builder),
2205        Instruction::XxDotStep => instruction_xx_dot_step(circuit_builder),
2206        Instruction::XbDotStep => instruction_xb_dot_step(circuit_builder),
2207    }
2208}
2209
2210/// Constrains instruction argument `nia` such that 0 < nia <= 5.
2211fn prohibit_any_illegal_number_of_words(
2212    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2213) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2214    let prohibitions = NumberOfWords::illegal_values()
2215        .map(|n| indicator_polynomial(circuit_builder, n))
2216        .into_iter()
2217        .sum();
2218
2219    vec![prohibitions]
2220}
2221
2222fn log_derivative_accumulates_clk_next(
2223    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2224) -> ConstraintCircuitMonad<DualRowIndicator> {
2225    let challenge = |c: ChallengeId| circuit_builder.challenge(c);
2226    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2227    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2228    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2229
2230    (next_aux_row(AuxColumn::ClockJumpDifferenceLookupServerLogDerivative)
2231        - curr_aux_row(AuxColumn::ClockJumpDifferenceLookupServerLogDerivative))
2232        * (challenge(ChallengeId::ClockJumpDifferenceLookupIndeterminate)
2233            - next_main_row(MainColumn::CLK))
2234        - next_main_row(MainColumn::ClockJumpDifferenceLookupMultiplicity)
2235}
2236
2237fn running_evaluation_for_standard_input_remains_unchanged(
2238    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2239) -> ConstraintCircuitMonad<DualRowIndicator> {
2240    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2241    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2242
2243    next_aux_row(AuxColumn::InputTableEvalArg) - curr_aux_row(AuxColumn::InputTableEvalArg)
2244}
2245
2246fn running_evaluation_for_standard_output_remains_unchanged(
2247    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2248) -> ConstraintCircuitMonad<DualRowIndicator> {
2249    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2250    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2251
2252    next_aux_row(AuxColumn::OutputTableEvalArg) - curr_aux_row(AuxColumn::OutputTableEvalArg)
2253}
2254
2255fn grow_stack_by_n_and_read_n_symbols_from_input(
2256    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2257    n: usize,
2258) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2259    let indeterminate = || circuit_builder.challenge(ChallengeId::StandardInputIndeterminate);
2260    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2261    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2262    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2263
2264    let mut running_evaluation = curr_aux_row(AuxColumn::InputTableEvalArg);
2265    for i in (0..n).rev() {
2266        let stack_element = ProcessorTable::op_stack_column_by_index(i);
2267        running_evaluation = indeterminate() * running_evaluation + next_main_row(stack_element);
2268    }
2269    let running_evaluation_update = next_aux_row(AuxColumn::InputTableEvalArg) - running_evaluation;
2270    let conditional_running_evaluation_update =
2271        indicator_polynomial(circuit_builder, n) * running_evaluation_update;
2272
2273    let mut constraints = conditional_constraints_for_growing_stack_by(circuit_builder, n);
2274    constraints.push(conditional_running_evaluation_update);
2275    constraints
2276}
2277
2278fn shrink_stack_by_n_and_write_n_symbols_to_output(
2279    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2280    n: usize,
2281) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2282    let indeterminate = || circuit_builder.challenge(ChallengeId::StandardOutputIndeterminate);
2283    let curr_main_row =
2284        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2285    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2286    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2287
2288    let mut running_evaluation = curr_aux_row(AuxColumn::OutputTableEvalArg);
2289    for i in 0..n {
2290        let stack_element = ProcessorTable::op_stack_column_by_index(i);
2291        running_evaluation = indeterminate() * running_evaluation + curr_main_row(stack_element);
2292    }
2293    let running_evaluation_update =
2294        next_aux_row(AuxColumn::OutputTableEvalArg) - running_evaluation;
2295    let conditional_running_evaluation_update =
2296        indicator_polynomial(circuit_builder, n) * running_evaluation_update;
2297
2298    let mut constraints = conditional_constraints_for_shrinking_stack_by(circuit_builder, n);
2299    constraints.push(conditional_running_evaluation_update);
2300    constraints
2301}
2302
2303fn log_derivative_for_instruction_lookup_updates_correctly(
2304    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2305) -> ConstraintCircuitMonad<DualRowIndicator> {
2306    let one = || circuit_builder.b_constant(1);
2307    let challenge = |c: ChallengeId| circuit_builder.challenge(c);
2308    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2309    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2310    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2311
2312    let compressed_row = challenge(ChallengeId::ProgramAddressWeight)
2313        * next_main_row(MainColumn::IP)
2314        + challenge(ChallengeId::ProgramInstructionWeight) * next_main_row(MainColumn::CI)
2315        + challenge(ChallengeId::ProgramNextInstructionWeight) * next_main_row(MainColumn::NIA);
2316    let log_derivative_updates = (next_aux_row(AuxColumn::InstructionLookupClientLogDerivative)
2317        - curr_aux_row(AuxColumn::InstructionLookupClientLogDerivative))
2318        * (challenge(ChallengeId::InstructionLookupIndeterminate) - compressed_row)
2319        - one();
2320    let log_derivative_remains = next_aux_row(AuxColumn::InstructionLookupClientLogDerivative)
2321        - curr_aux_row(AuxColumn::InstructionLookupClientLogDerivative);
2322
2323    (one() - next_main_row(MainColumn::IsPadding)) * log_derivative_updates
2324        + next_main_row(MainColumn::IsPadding) * log_derivative_remains
2325}
2326
2327fn constraints_for_shrinking_stack_by_3_and_top_3_unconstrained(
2328    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2329) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2330    let constant = |c: u64| circuit_builder.b_constant(c);
2331    let curr_main_row =
2332        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2333    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2334
2335    vec![
2336        next_main_row(MainColumn::ST3) - curr_main_row(MainColumn::ST6),
2337        next_main_row(MainColumn::ST4) - curr_main_row(MainColumn::ST7),
2338        next_main_row(MainColumn::ST5) - curr_main_row(MainColumn::ST8),
2339        next_main_row(MainColumn::ST6) - curr_main_row(MainColumn::ST9),
2340        next_main_row(MainColumn::ST7) - curr_main_row(MainColumn::ST10),
2341        next_main_row(MainColumn::ST8) - curr_main_row(MainColumn::ST11),
2342        next_main_row(MainColumn::ST9) - curr_main_row(MainColumn::ST12),
2343        next_main_row(MainColumn::ST10) - curr_main_row(MainColumn::ST13),
2344        next_main_row(MainColumn::ST11) - curr_main_row(MainColumn::ST14),
2345        next_main_row(MainColumn::ST12) - curr_main_row(MainColumn::ST15),
2346        next_main_row(MainColumn::OpStackPointer) - curr_main_row(MainColumn::OpStackPointer)
2347            + constant(3),
2348        running_product_op_stack_accounts_for_shrinking_stack_by(circuit_builder, 3),
2349    ]
2350}
2351
2352fn stack_shrinks_by_any_of(
2353    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2354    shrinkages: &[usize],
2355) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2356    let all_constraints_for_all_shrinkages = shrinkages
2357        .iter()
2358        .map(|&n| conditional_constraints_for_shrinking_stack_by(circuit_builder, n))
2359        .collect_vec();
2360
2361    combine_mutually_exclusive_constraint_groups(
2362        circuit_builder,
2363        all_constraints_for_all_shrinkages,
2364    )
2365}
2366
2367fn stack_grows_by_any_of(
2368    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2369    growths: &[usize],
2370) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2371    let all_constraints_for_all_growths = growths
2372        .iter()
2373        .map(|&n| conditional_constraints_for_growing_stack_by(circuit_builder, n))
2374        .collect_vec();
2375
2376    combine_mutually_exclusive_constraint_groups(circuit_builder, all_constraints_for_all_growths)
2377}
2378
2379/// Reduces the number of constraints by summing mutually exclusive constraints.
2380/// The mutual exclusion is due to the conditional nature of the constraints,
2381/// which has to be guaranteed by the caller.
2382///
2383/// For example, the constraints for shrinking the stack by 2, 3, and 4 elements
2384/// are:
2385///
2386/// ```markdown
2387/// |            shrink by 2 |            shrink by 3 |            shrink by 4 |
2388/// |-----------------------:|-----------------------:|-----------------------:|
2389/// |     ind_2·(st0' - st2) |     ind_3·(st0' - st3) |     ind_4·(st0' - st4) |
2390/// |     ind_2·(st1' - st3) |     ind_3·(st1' - st4) |     ind_4·(st1' - st5) |
2391/// |                      … |                      … |                      … |
2392/// |   ind_2·(st11' - st13) |   ind_3·(st11' - st14) |   ind_4·(st11' - st15) |
2393/// |   ind_2·(st12' - st14) |   ind_3·(st12' - st15) | ind_4·(osp' - osp + 4) |
2394/// |   ind_2·(st13' - st15) | ind_3·(osp' - osp + 3) | ind_4·(rp' - rp·fac_4) |
2395/// | ind_2·(osp' - osp + 2) | ind_3·(rp' - rp·fac_3) |                        |
2396/// | ind_2·(rp' - rp·fac_2) |                        |                        |
2397/// ```
2398///
2399/// This method sums these constraints “per row”. That is, the resulting
2400/// constraints are:
2401///
2402/// ```markdown
2403/// |                                                  shrink by 2 or 3 or 4 |
2404/// |-----------------------------------------------------------------------:|
2405/// |           ind_2·(st0' - st2) + ind_3·(st0' - st3) + ind_4·(st0' - st4) |
2406/// |           ind_2·(st1' - st3) + ind_3·(st1' - st4) + ind_4·(st1' - st5) |
2407/// |                                                                      … |
2408/// |     ind_2·(st11' - st13) + ind_3·(st11' - st14) + ind_4·(st11' - st15) |
2409/// |   ind_2·(st12' - st14) + ind_3·(st12' - st15) + ind_4·(osp' - osp + 4) |
2410/// | ind_2·(st13' - st15) + ind_3·(osp' - osp + 3) + ind_4·(rp' - rp·fac_4) |
2411/// |                        ind_2·(osp' - osp + 2) + ind_3·(rp' - rp·fac_3) |
2412/// |                                                 ind_2·(rp' - rp·fac_2) |
2413/// ```
2414///
2415/// Syntax in above example:
2416/// - `ind_n` is the [indicator polynomial](indicator_polynomial) for `n`
2417/// - `osp` is the [op stack pointer][osp]
2418/// - `rp` is the running product for the permutation argument
2419/// - `fac_n` is the factor for the running product
2420///
2421/// [osp]: crate::table_column::ProcessorMainColumn::OpStackPointer
2422fn combine_mutually_exclusive_constraint_groups(
2423    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2424    all_constraint_groups: Vec<Vec<ConstraintCircuitMonad<DualRowIndicator>>>,
2425) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2426    let constraint_group_lengths = all_constraint_groups.iter().map(|x| x.len());
2427    let num_constraints = constraint_group_lengths.max().unwrap_or(0);
2428
2429    let zero_constraint = || circuit_builder.b_constant(0);
2430    let mut combined_constraints = vec![];
2431    for i in 0..num_constraints {
2432        let combined_constraint = all_constraint_groups
2433            .iter()
2434            .filter_map(|constraint_group| constraint_group.get(i))
2435            .fold(zero_constraint(), |acc, summand| acc + summand.clone());
2436        combined_constraints.push(combined_constraint);
2437    }
2438    combined_constraints
2439}
2440
2441fn constraints_for_shrinking_stack_by(
2442    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2443    n: usize,
2444) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2445    let constant = |c: usize| circuit_builder.b_constant(u64::try_from(c).unwrap());
2446    let curr_row = |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2447    let next_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2448
2449    let stack = || (0..OpStackElement::COUNT).map(ProcessorTable::op_stack_column_by_index);
2450    let new_stack = stack().dropping_back(n).map(next_row).collect_vec();
2451    let old_stack_with_top_n_removed = stack().skip(n).map(curr_row).collect_vec();
2452
2453    let compress = |stack: Vec<_>| -> ConstraintCircuitMonad<_> {
2454        assert_eq!(OpStackElement::COUNT - n, stack.len());
2455        let weight = |i| circuit_builder.challenge(stack_weight_by_index(i));
2456        let enumerated_stack = stack.into_iter().enumerate();
2457        enumerated_stack.map(|(i, st)| weight(i) * st).sum()
2458    };
2459    let compressed_new_stack = compress(new_stack);
2460    let compressed_old_stack = compress(old_stack_with_top_n_removed);
2461
2462    let op_stack_pointer_shrinks_by_n =
2463        next_row(MainColumn::OpStackPointer) - curr_row(MainColumn::OpStackPointer) + constant(n);
2464    let new_stack_is_old_stack_with_top_n_removed = compressed_new_stack - compressed_old_stack;
2465
2466    vec![
2467        op_stack_pointer_shrinks_by_n,
2468        new_stack_is_old_stack_with_top_n_removed,
2469        running_product_op_stack_accounts_for_shrinking_stack_by(circuit_builder, n),
2470    ]
2471}
2472
2473fn constraints_for_growing_stack_by(
2474    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2475    n: usize,
2476) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2477    let constant = |c: usize| circuit_builder.b_constant(u32::try_from(c).unwrap());
2478    let curr_row = |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2479    let next_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2480
2481    let stack = || (0..OpStackElement::COUNT).map(ProcessorTable::op_stack_column_by_index);
2482    let new_stack = stack().skip(n).map(next_row).collect_vec();
2483    let old_stack_with_top_n_added = stack().map(curr_row).dropping_back(n).collect_vec();
2484
2485    let compress = |stack: Vec<_>| -> ConstraintCircuitMonad<_> {
2486        assert_eq!(OpStackElement::COUNT - n, stack.len());
2487        let weight = |i| circuit_builder.challenge(stack_weight_by_index(i));
2488        let enumerated_stack = stack.into_iter().enumerate();
2489        enumerated_stack.map(|(i, st)| weight(i) * st).sum()
2490    };
2491    let compressed_new_stack = compress(new_stack);
2492    let compressed_old_stack = compress(old_stack_with_top_n_added);
2493
2494    let op_stack_pointer_grows_by_n =
2495        next_row(MainColumn::OpStackPointer) - curr_row(MainColumn::OpStackPointer) - constant(n);
2496    let new_stack_is_old_stack_with_top_n_added = compressed_new_stack - compressed_old_stack;
2497
2498    vec![
2499        op_stack_pointer_grows_by_n,
2500        new_stack_is_old_stack_with_top_n_added,
2501        running_product_op_stack_accounts_for_growing_stack_by(circuit_builder, n),
2502    ]
2503}
2504
2505fn conditional_constraints_for_shrinking_stack_by(
2506    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2507    n: usize,
2508) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2509    constraints_for_shrinking_stack_by(circuit_builder, n)
2510        .into_iter()
2511        .map(|constraint| indicator_polynomial(circuit_builder, n) * constraint)
2512        .collect()
2513}
2514
2515fn conditional_constraints_for_growing_stack_by(
2516    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2517    n: usize,
2518) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2519    constraints_for_growing_stack_by(circuit_builder, n)
2520        .into_iter()
2521        .map(|constraint| indicator_polynomial(circuit_builder, n) * constraint)
2522        .collect()
2523}
2524
2525fn running_product_op_stack_accounts_for_growing_stack_by(
2526    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2527    n: usize,
2528) -> ConstraintCircuitMonad<DualRowIndicator> {
2529    let constant = |c: u32| circuit_builder.b_constant(c);
2530    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2531    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2532    let single_grow_factor = |op_stack_pointer_offset| {
2533        single_factor_for_permutation_argument_with_op_stack_table(
2534            circuit_builder,
2535            CurrentMain,
2536            op_stack_pointer_offset,
2537        )
2538    };
2539
2540    let mut factor = constant(1);
2541    for op_stack_pointer_offset in 0..n {
2542        factor = factor * single_grow_factor(op_stack_pointer_offset);
2543    }
2544
2545    next_aux_row(AuxColumn::OpStackTablePermArg)
2546        - curr_aux_row(AuxColumn::OpStackTablePermArg) * factor
2547}
2548
2549fn running_product_op_stack_accounts_for_shrinking_stack_by(
2550    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2551    n: usize,
2552) -> ConstraintCircuitMonad<DualRowIndicator> {
2553    let constant = |c: u32| circuit_builder.b_constant(c);
2554    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2555    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2556    let single_shrink_factor = |op_stack_pointer_offset| {
2557        single_factor_for_permutation_argument_with_op_stack_table(
2558            circuit_builder,
2559            NextMain,
2560            op_stack_pointer_offset,
2561        )
2562    };
2563
2564    let mut factor = constant(1);
2565    for op_stack_pointer_offset in 0..n {
2566        factor = factor * single_shrink_factor(op_stack_pointer_offset);
2567    }
2568
2569    next_aux_row(AuxColumn::OpStackTablePermArg)
2570        - curr_aux_row(AuxColumn::OpStackTablePermArg) * factor
2571}
2572
2573fn single_factor_for_permutation_argument_with_op_stack_table(
2574    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2575    row_with_shorter_stack_indicator: fn(usize) -> DualRowIndicator,
2576    op_stack_pointer_offset: usize,
2577) -> ConstraintCircuitMonad<DualRowIndicator> {
2578    let constant = |c: u32| circuit_builder.b_constant(c);
2579    let challenge = |c: ChallengeId| circuit_builder.challenge(c);
2580    let curr_main_row =
2581        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2582    let row_with_shorter_stack = |col: MainColumn| {
2583        circuit_builder.input(row_with_shorter_stack_indicator(col.master_main_index()))
2584    };
2585
2586    let max_stack_element_index = OpStackElement::COUNT - 1;
2587    let stack_element_index = max_stack_element_index - op_stack_pointer_offset;
2588    let stack_element = ProcessorTable::op_stack_column_by_index(stack_element_index);
2589    let underflow_element = row_with_shorter_stack(stack_element);
2590
2591    let op_stack_pointer = row_with_shorter_stack(MainColumn::OpStackPointer);
2592    let offset = constant(op_stack_pointer_offset as u32);
2593    let offset_op_stack_pointer = op_stack_pointer + offset;
2594
2595    let compressed_row = challenge(ChallengeId::OpStackClkWeight) * curr_main_row(MainColumn::CLK)
2596        + challenge(ChallengeId::OpStackIb1Weight) * curr_main_row(MainColumn::IB1)
2597        + challenge(ChallengeId::OpStackPointerWeight) * offset_op_stack_pointer
2598        + challenge(ChallengeId::OpStackFirstUnderflowElementWeight) * underflow_element;
2599    challenge(ChallengeId::OpStackIndeterminate) - compressed_row
2600}
2601
2602/// Build constraints for popping `n` elements from the top of the stack and
2603/// writing them to RAM. The reciprocal of [`read_from_ram_any_of`].
2604fn write_to_ram_any_of(
2605    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2606    number_of_words: &[usize],
2607) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2608    let all_constraint_groups = number_of_words
2609        .iter()
2610        .map(|&n| conditional_constraints_for_writing_n_elements_to_ram(circuit_builder, n))
2611        .collect_vec();
2612    combine_mutually_exclusive_constraint_groups(circuit_builder, all_constraint_groups)
2613}
2614
2615/// Build constraints for reading `n` elements from RAM and putting them on top
2616/// of the stack. The reciprocal of [`write_to_ram_any_of`].
2617///
2618/// To constrain RAM reads with more flexible target locations, see
2619/// [`read_from_ram_to`].
2620fn read_from_ram_any_of(
2621    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2622    number_of_words: &[usize],
2623) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2624    let all_constraint_groups = number_of_words
2625        .iter()
2626        .map(|&n| conditional_constraints_for_reading_n_elements_from_ram(circuit_builder, n))
2627        .collect_vec();
2628    combine_mutually_exclusive_constraint_groups(circuit_builder, all_constraint_groups)
2629}
2630
2631fn conditional_constraints_for_writing_n_elements_to_ram(
2632    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2633    n: usize,
2634) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2635    shrink_stack_by_n_and_write_n_elements_to_ram(circuit_builder, n)
2636        .into_iter()
2637        .map(|constraint| indicator_polynomial(circuit_builder, n) * constraint)
2638        .collect()
2639}
2640
2641fn conditional_constraints_for_reading_n_elements_from_ram(
2642    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2643    n: usize,
2644) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2645    grow_stack_by_n_and_read_n_elements_from_ram(circuit_builder, n)
2646        .into_iter()
2647        .map(|constraint| indicator_polynomial(circuit_builder, n) * constraint)
2648        .collect()
2649}
2650
2651fn shrink_stack_by_n_and_write_n_elements_to_ram(
2652    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2653    n: usize,
2654) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2655    let constant = |c: usize| circuit_builder.b_constant(u32::try_from(c).unwrap());
2656    let curr_main_row =
2657        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2658    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2659
2660    let op_stack_pointer_shrinks_by_n = next_main_row(MainColumn::OpStackPointer)
2661        - curr_main_row(MainColumn::OpStackPointer)
2662        + constant(n);
2663    let ram_pointer_grows_by_n =
2664        next_main_row(MainColumn::ST0) - curr_main_row(MainColumn::ST0) - constant(n);
2665
2666    let mut constraints = vec![
2667        op_stack_pointer_shrinks_by_n,
2668        ram_pointer_grows_by_n,
2669        running_product_op_stack_accounts_for_shrinking_stack_by(circuit_builder, n),
2670        running_product_ram_accounts_for_writing_n_elements(circuit_builder, n),
2671    ];
2672
2673    let num_ram_pointers = 1;
2674    for i in n + num_ram_pointers..OpStackElement::COUNT {
2675        let curr_stack_element = ProcessorTable::op_stack_column_by_index(i);
2676        let next_stack_element = ProcessorTable::op_stack_column_by_index(i - n);
2677        let element_i_is_shifted_by_n =
2678            next_main_row(next_stack_element) - curr_main_row(curr_stack_element);
2679        constraints.push(element_i_is_shifted_by_n);
2680    }
2681    constraints
2682}
2683
2684fn grow_stack_by_n_and_read_n_elements_from_ram(
2685    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2686    n: usize,
2687) -> Vec<ConstraintCircuitMonad<DualRowIndicator>> {
2688    let constant = |c: usize| circuit_builder.b_constant(u64::try_from(c).unwrap());
2689    let curr_main_row =
2690        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2691    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2692
2693    let op_stack_pointer_grows_by_n = next_main_row(MainColumn::OpStackPointer)
2694        - curr_main_row(MainColumn::OpStackPointer)
2695        - constant(n);
2696    let ram_pointer_shrinks_by_n =
2697        next_main_row(MainColumn::ST0) - curr_main_row(MainColumn::ST0) + constant(n);
2698
2699    let mut constraints = vec![
2700        op_stack_pointer_grows_by_n,
2701        ram_pointer_shrinks_by_n,
2702        running_product_op_stack_accounts_for_growing_stack_by(circuit_builder, n),
2703        running_product_ram_accounts_for_reading_n_elements(circuit_builder, n),
2704    ];
2705
2706    let num_ram_pointers = 1;
2707    for i in num_ram_pointers..OpStackElement::COUNT - n {
2708        let curr_stack_element = ProcessorTable::op_stack_column_by_index(i);
2709        let next_stack_element = ProcessorTable::op_stack_column_by_index(i + n);
2710        let element_i_is_shifted_by_n =
2711            next_main_row(next_stack_element) - curr_main_row(curr_stack_element);
2712        constraints.push(element_i_is_shifted_by_n);
2713    }
2714    constraints
2715}
2716
2717fn running_product_ram_accounts_for_writing_n_elements(
2718    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2719    n: usize,
2720) -> ConstraintCircuitMonad<DualRowIndicator> {
2721    let constant = |c: u32| circuit_builder.b_constant(c);
2722    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2723    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2724    let single_write_factor = |ram_pointer_offset| {
2725        single_factor_for_permutation_argument_with_ram_table(
2726            circuit_builder,
2727            CurrentMain,
2728            table::ram::INSTRUCTION_TYPE_WRITE,
2729            ram_pointer_offset,
2730        )
2731    };
2732
2733    let mut factor = constant(1);
2734    for ram_pointer_offset in 0..n {
2735        factor = factor * single_write_factor(ram_pointer_offset);
2736    }
2737
2738    next_aux_row(AuxColumn::RamTablePermArg) - curr_aux_row(AuxColumn::RamTablePermArg) * factor
2739}
2740
2741fn running_product_ram_accounts_for_reading_n_elements(
2742    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2743    n: usize,
2744) -> ConstraintCircuitMonad<DualRowIndicator> {
2745    let constant = |c: u32| circuit_builder.b_constant(c);
2746    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2747    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2748    let single_read_factor = |ram_pointer_offset| {
2749        single_factor_for_permutation_argument_with_ram_table(
2750            circuit_builder,
2751            NextMain,
2752            table::ram::INSTRUCTION_TYPE_READ,
2753            ram_pointer_offset,
2754        )
2755    };
2756
2757    let mut factor = constant(1);
2758    for ram_pointer_offset in 0..n {
2759        factor = factor * single_read_factor(ram_pointer_offset);
2760    }
2761
2762    next_aux_row(AuxColumn::RamTablePermArg) - curr_aux_row(AuxColumn::RamTablePermArg) * factor
2763}
2764
2765fn single_factor_for_permutation_argument_with_ram_table(
2766    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2767    row_with_longer_stack_indicator: fn(usize) -> DualRowIndicator,
2768    instruction_type: BFieldElement,
2769    ram_pointer_offset: usize,
2770) -> ConstraintCircuitMonad<DualRowIndicator> {
2771    let constant = |c: u32| circuit_builder.b_constant(c);
2772    let b_constant = |c| circuit_builder.b_constant(c);
2773    let challenge = |c: ChallengeId| circuit_builder.challenge(c);
2774    let curr_main_row =
2775        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2776    let row_with_longer_stack = |col: MainColumn| {
2777        circuit_builder.input(row_with_longer_stack_indicator(col.master_main_index()))
2778    };
2779
2780    let num_ram_pointers = 1;
2781    let ram_value_index = ram_pointer_offset + num_ram_pointers;
2782    let ram_value_column = ProcessorTable::op_stack_column_by_index(ram_value_index);
2783    let ram_value = row_with_longer_stack(ram_value_column);
2784
2785    let additional_offset = match instruction_type {
2786        table::ram::INSTRUCTION_TYPE_READ => 1,
2787        table::ram::INSTRUCTION_TYPE_WRITE => 0,
2788        _ => panic!("Invalid instruction type"),
2789    };
2790
2791    let ram_pointer = row_with_longer_stack(MainColumn::ST0);
2792    let offset = constant(additional_offset + ram_pointer_offset as u32);
2793    let offset_ram_pointer = ram_pointer + offset;
2794
2795    let compressed_row = curr_main_row(MainColumn::CLK) * challenge(ChallengeId::RamClkWeight)
2796        + b_constant(instruction_type) * challenge(ChallengeId::RamInstructionTypeWeight)
2797        + offset_ram_pointer * challenge(ChallengeId::RamPointerWeight)
2798        + ram_value * challenge(ChallengeId::RamValueWeight);
2799    challenge(ChallengeId::RamIndeterminate) - compressed_row
2800}
2801
2802fn running_product_for_jump_stack_table_updates_correctly(
2803    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2804) -> ConstraintCircuitMonad<DualRowIndicator> {
2805    let challenge = |c: ChallengeId| circuit_builder.challenge(c);
2806    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2807    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2808    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2809
2810    let compressed_row = challenge(ChallengeId::JumpStackClkWeight)
2811        * next_main_row(MainColumn::CLK)
2812        + challenge(ChallengeId::JumpStackCiWeight) * next_main_row(MainColumn::CI)
2813        + challenge(ChallengeId::JumpStackJspWeight) * next_main_row(MainColumn::JSP)
2814        + challenge(ChallengeId::JumpStackJsoWeight) * next_main_row(MainColumn::JSO)
2815        + challenge(ChallengeId::JumpStackJsdWeight) * next_main_row(MainColumn::JSD);
2816
2817    next_aux_row(AuxColumn::JumpStackTablePermArg)
2818        - curr_aux_row(AuxColumn::JumpStackTablePermArg)
2819            * (challenge(ChallengeId::JumpStackIndeterminate) - compressed_row)
2820}
2821
2822/// Deal with instructions `hash` and `merkle_step`. The registers from which
2823/// the preimage is loaded differs between the two instructions:
2824/// 1. `hash` always loads the stack's 10 top elements,
2825/// 1. `merkle_step` loads the stack's 5 top elements and helper variables 0
2826///    through 4. The order of those two quintuplets depends on helper variable
2827///    hv5.
2828///
2829/// The Hash Table does not “know” about instruction `merkle_step`.
2830///
2831/// Note that using `next_row` might be confusing at first glance; See the
2832/// [specification](https://triton-vm.org/spec/processor-table.html).
2833fn running_evaluation_hash_input_updates_correctly(
2834    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2835) -> ConstraintCircuitMonad<DualRowIndicator> {
2836    let constant = |c: u32| circuit_builder.b_constant(c);
2837    let one = || constant(1);
2838    let challenge = |c: ChallengeId| circuit_builder.challenge(c);
2839    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2840    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2841    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2842
2843    let hash_deselector = instruction_deselector_next_row(circuit_builder, Instruction::Hash);
2844    let merkle_step_deselector =
2845        instruction_deselector_next_row(circuit_builder, Instruction::MerkleStep);
2846    let merkle_step_mem_deselector =
2847        instruction_deselector_next_row(circuit_builder, Instruction::MerkleStepMem);
2848    let hash_and_merkle_step_selector = (next_main_row(MainColumn::CI)
2849        - constant(Instruction::Hash.opcode()))
2850        * (next_main_row(MainColumn::CI) - constant(Instruction::MerkleStep.opcode()))
2851        * (next_main_row(MainColumn::CI) - constant(Instruction::MerkleStepMem.opcode()));
2852
2853    let weights = [
2854        ChallengeId::StackWeight0,
2855        ChallengeId::StackWeight1,
2856        ChallengeId::StackWeight2,
2857        ChallengeId::StackWeight3,
2858        ChallengeId::StackWeight4,
2859        ChallengeId::StackWeight5,
2860        ChallengeId::StackWeight6,
2861        ChallengeId::StackWeight7,
2862        ChallengeId::StackWeight8,
2863        ChallengeId::StackWeight9,
2864    ]
2865    .map(challenge);
2866
2867    // hash
2868    let state_for_hash = [
2869        MainColumn::ST0,
2870        MainColumn::ST1,
2871        MainColumn::ST2,
2872        MainColumn::ST3,
2873        MainColumn::ST4,
2874        MainColumn::ST5,
2875        MainColumn::ST6,
2876        MainColumn::ST7,
2877        MainColumn::ST8,
2878        MainColumn::ST9,
2879    ]
2880    .map(next_main_row);
2881    let compressed_hash_row = weights
2882        .iter()
2883        .zip_eq(state_for_hash)
2884        .map(|(weight, state)| weight.clone() * state)
2885        .sum();
2886
2887    // merkle step
2888    let is_left_sibling = || next_main_row(MainColumn::HV5);
2889    let is_right_sibling = || one() - next_main_row(MainColumn::HV5);
2890    let merkle_step_state_element =
2891        |l, r| is_right_sibling() * next_main_row(l) + is_left_sibling() * next_main_row(r);
2892    let state_for_merkle_step = [
2893        merkle_step_state_element(MainColumn::ST0, MainColumn::HV0),
2894        merkle_step_state_element(MainColumn::ST1, MainColumn::HV1),
2895        merkle_step_state_element(MainColumn::ST2, MainColumn::HV2),
2896        merkle_step_state_element(MainColumn::ST3, MainColumn::HV3),
2897        merkle_step_state_element(MainColumn::ST4, MainColumn::HV4),
2898        merkle_step_state_element(MainColumn::HV0, MainColumn::ST0),
2899        merkle_step_state_element(MainColumn::HV1, MainColumn::ST1),
2900        merkle_step_state_element(MainColumn::HV2, MainColumn::ST2),
2901        merkle_step_state_element(MainColumn::HV3, MainColumn::ST3),
2902        merkle_step_state_element(MainColumn::HV4, MainColumn::ST4),
2903    ];
2904    let compressed_merkle_step_row = weights
2905        .into_iter()
2906        .zip_eq(state_for_merkle_step)
2907        .map(|(weight, state)| weight * state)
2908        .sum::<ConstraintCircuitMonad<_>>();
2909
2910    let running_evaluation_updates_with = |compressed_row| {
2911        next_aux_row(AuxColumn::HashInputEvalArg)
2912            - challenge(ChallengeId::HashInputIndeterminate)
2913                * curr_aux_row(AuxColumn::HashInputEvalArg)
2914            - compressed_row
2915    };
2916    let running_evaluation_remains =
2917        next_aux_row(AuxColumn::HashInputEvalArg) - curr_aux_row(AuxColumn::HashInputEvalArg);
2918
2919    hash_and_merkle_step_selector * running_evaluation_remains
2920        + hash_deselector * running_evaluation_updates_with(compressed_hash_row)
2921        + merkle_step_deselector
2922            * running_evaluation_updates_with(compressed_merkle_step_row.clone())
2923        + merkle_step_mem_deselector * running_evaluation_updates_with(compressed_merkle_step_row)
2924}
2925
2926fn running_evaluation_hash_digest_updates_correctly(
2927    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2928) -> ConstraintCircuitMonad<DualRowIndicator> {
2929    let constant = |c: u32| circuit_builder.b_constant(c);
2930    let challenge = |c: ChallengeId| circuit_builder.challenge(c);
2931    let curr_main_row =
2932        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2933    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2934    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2935    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2936
2937    let hash_deselector = instruction_deselector_current_row(circuit_builder, Instruction::Hash);
2938    let merkle_step_deselector =
2939        instruction_deselector_current_row(circuit_builder, Instruction::MerkleStep);
2940    let merkle_step_mem_deselector =
2941        instruction_deselector_current_row(circuit_builder, Instruction::MerkleStepMem);
2942    let hash_and_merkle_step_selector = (curr_main_row(MainColumn::CI)
2943        - constant(Instruction::Hash.opcode()))
2944        * (curr_main_row(MainColumn::CI) - constant(Instruction::MerkleStep.opcode()))
2945        * (curr_main_row(MainColumn::CI) - constant(Instruction::MerkleStepMem.opcode()));
2946
2947    let weights = [
2948        ChallengeId::StackWeight0,
2949        ChallengeId::StackWeight1,
2950        ChallengeId::StackWeight2,
2951        ChallengeId::StackWeight3,
2952        ChallengeId::StackWeight4,
2953    ]
2954    .map(challenge);
2955    let state = [
2956        MainColumn::ST0,
2957        MainColumn::ST1,
2958        MainColumn::ST2,
2959        MainColumn::ST3,
2960        MainColumn::ST4,
2961    ]
2962    .map(next_main_row);
2963    let compressed_row = weights
2964        .into_iter()
2965        .zip_eq(state)
2966        .map(|(weight, state)| weight * state)
2967        .sum();
2968
2969    let running_evaluation_updates = next_aux_row(AuxColumn::HashDigestEvalArg)
2970        - challenge(ChallengeId::HashDigestIndeterminate)
2971            * curr_aux_row(AuxColumn::HashDigestEvalArg)
2972        - compressed_row;
2973    let running_evaluation_remains =
2974        next_aux_row(AuxColumn::HashDigestEvalArg) - curr_aux_row(AuxColumn::HashDigestEvalArg);
2975
2976    hash_and_merkle_step_selector * running_evaluation_remains
2977        + (hash_deselector + merkle_step_deselector + merkle_step_mem_deselector)
2978            * running_evaluation_updates
2979}
2980
2981fn running_evaluation_sponge_updates_correctly(
2982    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
2983) -> ConstraintCircuitMonad<DualRowIndicator> {
2984    let constant = |c: u32| circuit_builder.b_constant(c);
2985    let challenge = |c: ChallengeId| circuit_builder.challenge(c);
2986    let curr_main_row =
2987        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
2988    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
2989    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
2990    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
2991
2992    let sponge_init_deselector =
2993        instruction_deselector_current_row(circuit_builder, Instruction::SpongeInit);
2994    let sponge_absorb_deselector =
2995        instruction_deselector_current_row(circuit_builder, Instruction::SpongeAbsorb);
2996    let sponge_absorb_mem_deselector =
2997        instruction_deselector_current_row(circuit_builder, Instruction::SpongeAbsorbMem);
2998    let sponge_squeeze_deselector =
2999        instruction_deselector_current_row(circuit_builder, Instruction::SpongeSqueeze);
3000
3001    let sponge_instruction_selector = (curr_main_row(MainColumn::CI)
3002        - constant(Instruction::SpongeInit.opcode()))
3003        * (curr_main_row(MainColumn::CI) - constant(Instruction::SpongeAbsorb.opcode()))
3004        * (curr_main_row(MainColumn::CI) - constant(Instruction::SpongeAbsorbMem.opcode()))
3005        * (curr_main_row(MainColumn::CI) - constant(Instruction::SpongeSqueeze.opcode()));
3006
3007    let weighted_sum = |state| {
3008        let weights = [
3009            ChallengeId::StackWeight0,
3010            ChallengeId::StackWeight1,
3011            ChallengeId::StackWeight2,
3012            ChallengeId::StackWeight3,
3013            ChallengeId::StackWeight4,
3014            ChallengeId::StackWeight5,
3015            ChallengeId::StackWeight6,
3016            ChallengeId::StackWeight7,
3017            ChallengeId::StackWeight8,
3018            ChallengeId::StackWeight9,
3019        ];
3020        let weights = weights.map(challenge).into_iter();
3021        weights.zip_eq(state).map(|(w, st)| w * st).sum()
3022    };
3023
3024    let state = [
3025        MainColumn::ST0,
3026        MainColumn::ST1,
3027        MainColumn::ST2,
3028        MainColumn::ST3,
3029        MainColumn::ST4,
3030        MainColumn::ST5,
3031        MainColumn::ST6,
3032        MainColumn::ST7,
3033        MainColumn::ST8,
3034        MainColumn::ST9,
3035    ];
3036    let compressed_row_current = weighted_sum(state.map(curr_main_row));
3037    let compressed_row_next = weighted_sum(state.map(next_main_row));
3038
3039    // Use domain-specific knowledge: the compressed row (i.e., random linear
3040    // sum) of the initial Sponge state is 0.
3041    let running_evaluation_updates_for_sponge_init = next_aux_row(AuxColumn::SpongeEvalArg)
3042        - challenge(ChallengeId::SpongeIndeterminate) * curr_aux_row(AuxColumn::SpongeEvalArg)
3043        - challenge(ChallengeId::HashCIWeight) * curr_main_row(MainColumn::CI);
3044    let running_evaluation_updates_for_absorb =
3045        running_evaluation_updates_for_sponge_init.clone() - compressed_row_current;
3046    let running_evaluation_updates_for_squeeze =
3047        running_evaluation_updates_for_sponge_init.clone() - compressed_row_next;
3048    let running_evaluation_remains =
3049        next_aux_row(AuxColumn::SpongeEvalArg) - curr_aux_row(AuxColumn::SpongeEvalArg);
3050
3051    // `sponge_absorb_mem`
3052    let stack_elements = [
3053        MainColumn::ST1,
3054        MainColumn::ST2,
3055        MainColumn::ST3,
3056        MainColumn::ST4,
3057    ]
3058    .map(next_main_row);
3059    let hv_elements = [
3060        MainColumn::HV0,
3061        MainColumn::HV1,
3062        MainColumn::HV2,
3063        MainColumn::HV3,
3064        MainColumn::HV4,
3065        MainColumn::HV5,
3066    ]
3067    .map(curr_main_row);
3068    let absorb_mem_elements = stack_elements.into_iter().chain(hv_elements);
3069    let absorb_mem_elements = absorb_mem_elements.collect_vec().try_into().unwrap();
3070    let compressed_row_absorb_mem = weighted_sum(absorb_mem_elements);
3071    let running_evaluation_updates_for_absorb_mem = next_aux_row(AuxColumn::SpongeEvalArg)
3072        - challenge(ChallengeId::SpongeIndeterminate) * curr_aux_row(AuxColumn::SpongeEvalArg)
3073        - challenge(ChallengeId::HashCIWeight) * constant(Instruction::SpongeAbsorb.opcode())
3074        - compressed_row_absorb_mem;
3075
3076    sponge_instruction_selector * running_evaluation_remains
3077        + sponge_init_deselector * running_evaluation_updates_for_sponge_init
3078        + sponge_absorb_deselector * running_evaluation_updates_for_absorb
3079        + sponge_absorb_mem_deselector * running_evaluation_updates_for_absorb_mem
3080        + sponge_squeeze_deselector * running_evaluation_updates_for_squeeze
3081}
3082
3083fn log_derivative_with_u32_table_updates_correctly(
3084    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
3085) -> ConstraintCircuitMonad<DualRowIndicator> {
3086    let constant = |c: u32| circuit_builder.b_constant(c);
3087    let one = || constant(1);
3088    let two_inverse = circuit_builder.b_constant(bfe!(2).inverse());
3089    let challenge = |c: ChallengeId| circuit_builder.challenge(c);
3090    let curr_main_row =
3091        |col: MainColumn| circuit_builder.input(CurrentMain(col.master_main_index()));
3092    let next_main_row = |col: MainColumn| circuit_builder.input(NextMain(col.master_main_index()));
3093    let curr_aux_row = |col: AuxColumn| circuit_builder.input(CurrentAux(col.master_aux_index()));
3094    let next_aux_row = |col: AuxColumn| circuit_builder.input(NextAux(col.master_aux_index()));
3095
3096    let split_deselector = instruction_deselector_current_row(circuit_builder, Instruction::Split);
3097    let lt_deselector = instruction_deselector_current_row(circuit_builder, Instruction::Lt);
3098    let and_deselector = instruction_deselector_current_row(circuit_builder, Instruction::And);
3099    let xor_deselector = instruction_deselector_current_row(circuit_builder, Instruction::Xor);
3100    let pow_deselector = instruction_deselector_current_row(circuit_builder, Instruction::Pow);
3101    let log_2_floor_deselector =
3102        instruction_deselector_current_row(circuit_builder, Instruction::Log2Floor);
3103    let div_mod_deselector =
3104        instruction_deselector_current_row(circuit_builder, Instruction::DivMod);
3105    let pop_count_deselector =
3106        instruction_deselector_current_row(circuit_builder, Instruction::PopCount);
3107    let merkle_step_deselector =
3108        instruction_deselector_current_row(circuit_builder, Instruction::MerkleStep);
3109    let merkle_step_mem_deselector =
3110        instruction_deselector_current_row(circuit_builder, Instruction::MerkleStepMem);
3111
3112    let running_sum = curr_aux_row(AuxColumn::U32LookupClientLogDerivative);
3113    let running_sum_next = next_aux_row(AuxColumn::U32LookupClientLogDerivative);
3114
3115    let split_factor = challenge(ChallengeId::U32Indeterminate)
3116        - challenge(ChallengeId::U32LhsWeight) * next_main_row(MainColumn::ST0)
3117        - challenge(ChallengeId::U32RhsWeight) * next_main_row(MainColumn::ST1)
3118        - challenge(ChallengeId::U32CiWeight) * curr_main_row(MainColumn::CI);
3119    let binop_factor = challenge(ChallengeId::U32Indeterminate)
3120        - challenge(ChallengeId::U32LhsWeight) * curr_main_row(MainColumn::ST0)
3121        - challenge(ChallengeId::U32RhsWeight) * curr_main_row(MainColumn::ST1)
3122        - challenge(ChallengeId::U32CiWeight) * curr_main_row(MainColumn::CI)
3123        - challenge(ChallengeId::U32ResultWeight) * next_main_row(MainColumn::ST0);
3124    let xor_factor = challenge(ChallengeId::U32Indeterminate)
3125        - challenge(ChallengeId::U32LhsWeight) * curr_main_row(MainColumn::ST0)
3126        - challenge(ChallengeId::U32RhsWeight) * curr_main_row(MainColumn::ST1)
3127        - challenge(ChallengeId::U32CiWeight) * constant(Instruction::And.opcode())
3128        - challenge(ChallengeId::U32ResultWeight)
3129            * (curr_main_row(MainColumn::ST0) + curr_main_row(MainColumn::ST1)
3130                - next_main_row(MainColumn::ST0))
3131            * two_inverse;
3132    let unop_factor = challenge(ChallengeId::U32Indeterminate)
3133        - challenge(ChallengeId::U32LhsWeight) * curr_main_row(MainColumn::ST0)
3134        - challenge(ChallengeId::U32CiWeight) * curr_main_row(MainColumn::CI)
3135        - challenge(ChallengeId::U32ResultWeight) * next_main_row(MainColumn::ST0);
3136    let div_mod_factor_for_lt = challenge(ChallengeId::U32Indeterminate)
3137        - challenge(ChallengeId::U32LhsWeight) * next_main_row(MainColumn::ST0)
3138        - challenge(ChallengeId::U32RhsWeight) * curr_main_row(MainColumn::ST1)
3139        - challenge(ChallengeId::U32CiWeight) * constant(Instruction::Lt.opcode())
3140        - challenge(ChallengeId::U32ResultWeight);
3141    let div_mod_factor_for_range_check = challenge(ChallengeId::U32Indeterminate)
3142        - challenge(ChallengeId::U32LhsWeight) * curr_main_row(MainColumn::ST0)
3143        - challenge(ChallengeId::U32RhsWeight) * next_main_row(MainColumn::ST1)
3144        - challenge(ChallengeId::U32CiWeight) * constant(Instruction::Split.opcode());
3145    let merkle_step_range_check_factor = challenge(ChallengeId::U32Indeterminate)
3146        - challenge(ChallengeId::U32LhsWeight) * curr_main_row(MainColumn::ST5)
3147        - challenge(ChallengeId::U32RhsWeight) * next_main_row(MainColumn::ST5)
3148        - challenge(ChallengeId::U32CiWeight) * constant(Instruction::Split.opcode());
3149
3150    let running_sum_absorbs_split_factor =
3151        (running_sum_next.clone() - running_sum.clone()) * split_factor - one();
3152    let running_sum_absorbs_binop_factor =
3153        (running_sum_next.clone() - running_sum.clone()) * binop_factor - one();
3154    let running_sum_absorb_xor_factor =
3155        (running_sum_next.clone() - running_sum.clone()) * xor_factor - one();
3156    let running_sum_absorbs_unop_factor =
3157        (running_sum_next.clone() - running_sum.clone()) * unop_factor - one();
3158    let running_sum_absorbs_merkle_step_factor =
3159        (running_sum_next.clone() - running_sum.clone()) * merkle_step_range_check_factor - one();
3160
3161    let split_summand = split_deselector * running_sum_absorbs_split_factor;
3162    let lt_summand = lt_deselector * running_sum_absorbs_binop_factor.clone();
3163    let and_summand = and_deselector * running_sum_absorbs_binop_factor.clone();
3164    let xor_summand = xor_deselector * running_sum_absorb_xor_factor;
3165    let pow_summand = pow_deselector * running_sum_absorbs_binop_factor;
3166    let log_2_floor_summand = log_2_floor_deselector * running_sum_absorbs_unop_factor.clone();
3167    let div_mod_summand = div_mod_deselector
3168        * ((running_sum_next.clone() - running_sum.clone())
3169            * div_mod_factor_for_lt.clone()
3170            * div_mod_factor_for_range_check.clone()
3171            - div_mod_factor_for_lt
3172            - div_mod_factor_for_range_check);
3173    let pop_count_summand = pop_count_deselector * running_sum_absorbs_unop_factor;
3174    let merkle_step_summand =
3175        merkle_step_deselector * running_sum_absorbs_merkle_step_factor.clone();
3176    let merkle_step_mem_summand =
3177        merkle_step_mem_deselector * running_sum_absorbs_merkle_step_factor;
3178    let no_update_summand =
3179        (one() - curr_main_row(MainColumn::IB2)) * (running_sum_next - running_sum);
3180
3181    split_summand
3182        + lt_summand
3183        + and_summand
3184        + xor_summand
3185        + pow_summand
3186        + log_2_floor_summand
3187        + div_mod_summand
3188        + pop_count_summand
3189        + merkle_step_summand
3190        + merkle_step_mem_summand
3191        + no_update_summand
3192}
3193
3194fn stack_weight_by_index(i: usize) -> ChallengeId {
3195    match i {
3196        0 => ChallengeId::StackWeight0,
3197        1 => ChallengeId::StackWeight1,
3198        2 => ChallengeId::StackWeight2,
3199        3 => ChallengeId::StackWeight3,
3200        4 => ChallengeId::StackWeight4,
3201        5 => ChallengeId::StackWeight5,
3202        6 => ChallengeId::StackWeight6,
3203        7 => ChallengeId::StackWeight7,
3204        8 => ChallengeId::StackWeight8,
3205        9 => ChallengeId::StackWeight9,
3206        10 => ChallengeId::StackWeight10,
3207        11 => ChallengeId::StackWeight11,
3208        12 => ChallengeId::StackWeight12,
3209        13 => ChallengeId::StackWeight13,
3210        14 => ChallengeId::StackWeight14,
3211        15 => ChallengeId::StackWeight15,
3212        i => panic!("Op Stack weight index must be in [0, 15], not {i}."),
3213    }
3214}
3215
3216/// A polynomial that is 1 when evaluated on the given index, and 0 otherwise.
3217fn indicator_polynomial(
3218    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
3219    index: usize,
3220) -> ConstraintCircuitMonad<DualRowIndicator> {
3221    let one = || circuit_builder.b_constant(1);
3222    let hv = |idx| helper_variable(circuit_builder, idx);
3223
3224    match index {
3225        0 => (one() - hv(3)) * (one() - hv(2)) * (one() - hv(1)) * (one() - hv(0)),
3226        1 => (one() - hv(3)) * (one() - hv(2)) * (one() - hv(1)) * hv(0),
3227        2 => (one() - hv(3)) * (one() - hv(2)) * hv(1) * (one() - hv(0)),
3228        3 => (one() - hv(3)) * (one() - hv(2)) * hv(1) * hv(0),
3229        4 => (one() - hv(3)) * hv(2) * (one() - hv(1)) * (one() - hv(0)),
3230        5 => (one() - hv(3)) * hv(2) * (one() - hv(1)) * hv(0),
3231        6 => (one() - hv(3)) * hv(2) * hv(1) * (one() - hv(0)),
3232        7 => (one() - hv(3)) * hv(2) * hv(1) * hv(0),
3233        8 => hv(3) * (one() - hv(2)) * (one() - hv(1)) * (one() - hv(0)),
3234        9 => hv(3) * (one() - hv(2)) * (one() - hv(1)) * hv(0),
3235        10 => hv(3) * (one() - hv(2)) * hv(1) * (one() - hv(0)),
3236        11 => hv(3) * (one() - hv(2)) * hv(1) * hv(0),
3237        12 => hv(3) * hv(2) * (one() - hv(1)) * (one() - hv(0)),
3238        13 => hv(3) * hv(2) * (one() - hv(1)) * hv(0),
3239        14 => hv(3) * hv(2) * hv(1) * (one() - hv(0)),
3240        15 => hv(3) * hv(2) * hv(1) * hv(0),
3241        i => panic!("indicator polynomial index {i} out of bounds"),
3242    }
3243}
3244
3245fn helper_variable(
3246    circuit_builder: &ConstraintCircuitBuilder<DualRowIndicator>,
3247    index: usize,
3248) -> ConstraintCircuitMonad<DualRowIndicator> {
3249    match index {
3250        0 => circuit_builder.input(CurrentMain(MainColumn::HV0.master_main_index())),
3251        1 => circuit_builder.input(CurrentMain(MainColumn::HV1.master_main_index())),
3252        2 => circuit_builder.input(CurrentMain(MainColumn::HV2.master_main_index())),
3253        3 => circuit_builder.input(CurrentMain(MainColumn::HV3.master_main_index())),
3254        4 => circuit_builder.input(CurrentMain(MainColumn::HV4.master_main_index())),
3255        5 => circuit_builder.input(CurrentMain(MainColumn::HV5.master_main_index())),
3256        i => unimplemented!("Helper variable index {i} out of bounds."),
3257    }
3258}
3259
3260#[cfg(test)]
3261#[cfg_attr(coverage_nightly, coverage(off))]
3262mod tests {
3263    use ndarray::Array2;
3264    use ndarray::s;
3265    use num_traits::identities::Zero;
3266    use proptest::prop_assert_eq;
3267    use proptest_arbitrary_interop::arb;
3268    use test_strategy::proptest;
3269
3270    use crate::table::NUM_AUX_COLUMNS;
3271    use crate::table::NUM_MAIN_COLUMNS;
3272
3273    use super::*;
3274
3275    type MainColumn = <ProcessorTable as AIR>::MainColumn;
3276
3277    #[test]
3278    fn instruction_deselector_gives_0_for_all_other_instructions() {
3279        let circuit_builder = ConstraintCircuitBuilder::new();
3280
3281        let mut master_main_table = Array2::zeros([2, NUM_MAIN_COLUMNS]);
3282        let master_aux_table = Array2::zeros([2, NUM_AUX_COLUMNS]);
3283
3284        // For this test, dummy challenges suffice to evaluate the constraints.
3285        let dummy_challenges = (0..ChallengeId::COUNT)
3286            .map(|i| XFieldElement::from(i as u64))
3287            .collect_vec();
3288        for instruction in ALL_INSTRUCTIONS {
3289            let deselector = instruction_deselector_current_row(&circuit_builder, instruction);
3290
3291            println!("\n\nThe Deselector for instruction {instruction} is:\n{deselector}",);
3292
3293            // Negative tests
3294            for other_instruction in ALL_INSTRUCTIONS
3295                .into_iter()
3296                .filter(|other_instruction| *other_instruction != instruction)
3297            {
3298                let mut curr_row = master_main_table.slice_mut(s![0, ..]);
3299                curr_row[MainColumn::IB0.master_main_index()] =
3300                    other_instruction.ib(InstructionBit::IB0);
3301                curr_row[MainColumn::IB1.master_main_index()] =
3302                    other_instruction.ib(InstructionBit::IB1);
3303                curr_row[MainColumn::IB2.master_main_index()] =
3304                    other_instruction.ib(InstructionBit::IB2);
3305                curr_row[MainColumn::IB3.master_main_index()] =
3306                    other_instruction.ib(InstructionBit::IB3);
3307                curr_row[MainColumn::IB4.master_main_index()] =
3308                    other_instruction.ib(InstructionBit::IB4);
3309                curr_row[MainColumn::IB5.master_main_index()] =
3310                    other_instruction.ib(InstructionBit::IB5);
3311                curr_row[MainColumn::IB6.master_main_index()] =
3312                    other_instruction.ib(InstructionBit::IB6);
3313                let result = deselector.clone().consume().evaluate(
3314                    master_main_table.view(),
3315                    master_aux_table.view(),
3316                    &dummy_challenges,
3317                );
3318
3319                assert!(
3320                    result.is_zero(),
3321                    "Deselector for {instruction} should return 0 for all other instructions, \
3322                    including {other_instruction} whose opcode is {}",
3323                    other_instruction.opcode()
3324                )
3325            }
3326
3327            // Positive tests
3328            let mut curr_row = master_main_table.slice_mut(s![0, ..]);
3329            curr_row[MainColumn::IB0.master_main_index()] = instruction.ib(InstructionBit::IB0);
3330            curr_row[MainColumn::IB1.master_main_index()] = instruction.ib(InstructionBit::IB1);
3331            curr_row[MainColumn::IB2.master_main_index()] = instruction.ib(InstructionBit::IB2);
3332            curr_row[MainColumn::IB3.master_main_index()] = instruction.ib(InstructionBit::IB3);
3333            curr_row[MainColumn::IB4.master_main_index()] = instruction.ib(InstructionBit::IB4);
3334            curr_row[MainColumn::IB5.master_main_index()] = instruction.ib(InstructionBit::IB5);
3335            curr_row[MainColumn::IB6.master_main_index()] = instruction.ib(InstructionBit::IB6);
3336            let result = deselector.consume().evaluate(
3337                master_main_table.view(),
3338                master_aux_table.view(),
3339                &dummy_challenges,
3340            );
3341            assert!(
3342                !result.is_zero(),
3343                "Deselector for {instruction} should be non-zero when CI is {}",
3344                instruction.opcode()
3345            )
3346        }
3347    }
3348
3349    #[test]
3350    fn print_number_and_degrees_of_transition_constraints_for_all_instructions() {
3351        println!();
3352        println!("| Instruction         | #polys | max deg | Degrees");
3353        println!("|:--------------------|-------:|--------:|:------------");
3354        let circuit_builder = ConstraintCircuitBuilder::new();
3355        for instruction in ALL_INSTRUCTIONS {
3356            let constraints = transition_constraints_for_instruction(&circuit_builder, instruction);
3357            let degrees = constraints
3358                .iter()
3359                .map(|circuit| circuit.clone().consume().degree())
3360                .collect_vec();
3361            let max_degree = degrees.iter().max().unwrap_or(&0);
3362            let degrees_str = degrees.iter().join(", ");
3363            println!(
3364                "| {:<19} | {:>6} | {max_degree:>7} | [{degrees_str}]",
3365                format!("{instruction}"),
3366                constraints.len(),
3367            );
3368        }
3369    }
3370    #[test]
3371    fn range_check_for_skiz_is_as_efficient_as_possible() {
3372        let range_check_constraints = next_instruction_range_check_constraints_for_instruction_skiz(
3373            &ConstraintCircuitBuilder::new(),
3374        );
3375        let range_check_constraints = range_check_constraints.iter();
3376        let all_degrees = range_check_constraints.map(|c| c.clone().consume().degree());
3377        let max_constraint_degree = all_degrees.max().unwrap_or(0);
3378        assert!(
3379            crate::TARGET_DEGREE <= max_constraint_degree,
3380            "Can the range check constraints be of a higher degree, saving columns?"
3381        );
3382    }
3383
3384    #[test]
3385    fn helper_variables_in_bounds() {
3386        let circuit_builder = ConstraintCircuitBuilder::new();
3387        for index in 0..NUM_HELPER_VARIABLE_REGISTERS {
3388            helper_variable(&circuit_builder, index);
3389        }
3390    }
3391
3392    #[proptest]
3393    #[should_panic(expected = "out of bounds")]
3394    fn cannot_get_helper_variable_for_out_of_range_index(
3395        #[strategy(NUM_HELPER_VARIABLE_REGISTERS..)] index: usize,
3396    ) {
3397        let circuit_builder = ConstraintCircuitBuilder::new();
3398        helper_variable(&circuit_builder, index);
3399    }
3400
3401    #[test]
3402    fn indicator_polynomial_in_bounds() {
3403        let circuit_builder = ConstraintCircuitBuilder::new();
3404        for index in 0..16 {
3405            indicator_polynomial(&circuit_builder, index);
3406        }
3407    }
3408
3409    #[proptest]
3410    #[should_panic(expected = "out of bounds")]
3411    fn cannot_get_indicator_polynomial_for_out_of_range_index(
3412        #[strategy(16_usize..
3413        )]
3414        index: usize,
3415    ) {
3416        let circuit_builder = ConstraintCircuitBuilder::new();
3417        indicator_polynomial(&circuit_builder, index);
3418    }
3419
3420    #[proptest]
3421    fn indicator_polynomial_is_one_on_indicated_index_and_zero_on_all_other_indices(
3422        #[strategy(0_usize..16)] indicator_poly_index: usize,
3423        #[strategy(0_u64..16)] query_index: u64,
3424    ) {
3425        let mut main_table = Array2::ones([2, NUM_MAIN_COLUMNS]);
3426        let aux_table = Array2::ones([2, NUM_AUX_COLUMNS]);
3427
3428        main_table[[0, MainColumn::HV0.master_main_index()]] = bfe!(query_index % 2);
3429        main_table[[0, MainColumn::HV1.master_main_index()]] = bfe!((query_index >> 1) % 2);
3430        main_table[[0, MainColumn::HV2.master_main_index()]] = bfe!((query_index >> 2) % 2);
3431        main_table[[0, MainColumn::HV3.master_main_index()]] = bfe!((query_index >> 3) % 2);
3432
3433        let builder = ConstraintCircuitBuilder::new();
3434        let indicator_poly = indicator_polynomial(&builder, indicator_poly_index).consume();
3435        let evaluation = indicator_poly.evaluate(main_table.view(), aux_table.view(), &[]);
3436
3437        if indicator_poly_index as u64 == query_index {
3438            prop_assert_eq!(xfe!(1), evaluation);
3439        } else {
3440            prop_assert_eq!(xfe!(0), evaluation);
3441        }
3442    }
3443
3444    #[test]
3445    fn can_get_op_stack_column_for_in_range_index() {
3446        for index in 0..OpStackElement::COUNT {
3447            let _ = ProcessorTable::op_stack_column_by_index(index);
3448        }
3449    }
3450
3451    #[proptest]
3452    #[should_panic(expected = "[0, 15]")]
3453    fn cannot_get_op_stack_column_for_out_of_range_index(
3454        #[strategy(OpStackElement::COUNT..)] index: usize,
3455    ) {
3456        let _ = ProcessorTable::op_stack_column_by_index(index);
3457    }
3458
3459    #[test]
3460    fn can_get_stack_weight_for_in_range_index() {
3461        for index in 0..OpStackElement::COUNT {
3462            let _ = stack_weight_by_index(index);
3463        }
3464    }
3465
3466    #[proptest]
3467    #[should_panic(expected = "[0, 15]")]
3468    fn cannot_get_stack_weight_for_out_of_range_index(
3469        #[strategy(OpStackElement::COUNT..)] index: usize,
3470    ) {
3471        let _ = stack_weight_by_index(index);
3472    }
3473
3474    #[proptest]
3475    fn xx_product_is_accurate(
3476        #[strategy(arb())] a: XFieldElement,
3477        #[strategy(arb())] b: XFieldElement,
3478    ) {
3479        let circuit_builder = ConstraintCircuitBuilder::new();
3480        let main_row = |col: MainColumn| circuit_builder.input(Main(col.master_main_index()));
3481        let [x0, x1, x2, y0, y1, y2] = [
3482            MainColumn::ST0,
3483            MainColumn::ST1,
3484            MainColumn::ST2,
3485            MainColumn::ST3,
3486            MainColumn::ST4,
3487            MainColumn::ST5,
3488        ]
3489        .map(main_row);
3490
3491        let mut main_table = Array2::zeros([1, NUM_MAIN_COLUMNS]);
3492        let aux_table = Array2::zeros([1, NUM_AUX_COLUMNS]);
3493        main_table[[0, MainColumn::ST0.master_main_index()]] = a.coefficients[0];
3494        main_table[[0, MainColumn::ST1.master_main_index()]] = a.coefficients[1];
3495        main_table[[0, MainColumn::ST2.master_main_index()]] = a.coefficients[2];
3496        main_table[[0, MainColumn::ST3.master_main_index()]] = b.coefficients[0];
3497        main_table[[0, MainColumn::ST4.master_main_index()]] = b.coefficients[1];
3498        main_table[[0, MainColumn::ST5.master_main_index()]] = b.coefficients[2];
3499
3500        let [c0, c1, c2] = xx_product([x0, x1, x2], [y0, y1, y2])
3501            .map(|c| c.consume())
3502            .map(|c| c.evaluate(main_table.view(), aux_table.view(), &[]))
3503            .map(|xfe| xfe.unlift().unwrap());
3504
3505        let c = a * b;
3506        prop_assert_eq!(c.coefficients[0], c0);
3507        prop_assert_eq!(c.coefficients[1], c1);
3508        prop_assert_eq!(c.coefficients[2], c2);
3509    }
3510
3511    #[proptest]
3512    fn xb_product_is_accurate(
3513        #[strategy(arb())] a: XFieldElement,
3514        #[strategy(arb())] b: BFieldElement,
3515    ) {
3516        let circuit_builder = ConstraintCircuitBuilder::new();
3517        let main_row = |col: MainColumn| circuit_builder.input(Main(col.master_main_index()));
3518        let [x0, x1, x2, y] = [
3519            MainColumn::ST0,
3520            MainColumn::ST1,
3521            MainColumn::ST2,
3522            MainColumn::ST3,
3523        ]
3524        .map(main_row);
3525
3526        let mut main_table = Array2::zeros([1, NUM_MAIN_COLUMNS]);
3527        let aux_table = Array2::zeros([1, NUM_AUX_COLUMNS]);
3528        main_table[[0, MainColumn::ST0.master_main_index()]] = a.coefficients[0];
3529        main_table[[0, MainColumn::ST1.master_main_index()]] = a.coefficients[1];
3530        main_table[[0, MainColumn::ST2.master_main_index()]] = a.coefficients[2];
3531        main_table[[0, MainColumn::ST3.master_main_index()]] = b;
3532
3533        let [c0, c1, c2] = xb_product([x0, x1, x2], y)
3534            .map(|c| c.consume())
3535            .map(|c| c.evaluate(main_table.view(), aux_table.view(), &[]))
3536            .map(|xfe| xfe.unlift().unwrap());
3537
3538        let c = a * b;
3539        prop_assert_eq!(c.coefficients[0], c0);
3540        prop_assert_eq!(c.coefficients[1], c1);
3541        prop_assert_eq!(c.coefficients[2], c2);
3542    }
3543}