use alloc::vec::Vec;
use miden_air::trace::{
AUX_TRACE_RAND_CHALLENGES, Challenges,
chiplets::hasher::HASH_CYCLE_LEN_FELT,
decoder::{P1_COL_IDX, P2_COL_IDX, P3_COL_IDX},
};
use miden_utils_testing::rand::rand_array;
use super::super::{
decoder::{BlockHashTableRow, build_op_group},
tests::{build_trace_from_ops, build_trace_from_program},
utils::build_span_with_respan_ops,
};
use crate::{
ContextId, Felt, ONE, Program, Word, ZERO,
field::{ExtensionField, Field},
mast::{
BasicBlockNodeBuilder, JoinNodeBuilder, LoopNodeBuilder, MastForest, MastForestContributor,
MastNodeExt, SplitNodeBuilder,
},
operation::Operation,
};
#[test]
#[expect(clippy::needless_range_loop)]
fn decoder_p1_span_with_respan() {
let (ops, _) = build_span_with_respan_ops();
let trace = build_trace_from_ops(ops, &[]);
let challenges = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_columns = trace.build_aux_trace(&challenges).unwrap();
let p1 = aux_columns.get_column(P1_COL_IDX);
let challenges = Challenges::<Felt>::new(challenges[0], challenges[1]);
let row_values = [
BlockStackTableRow::new(ONE, ZERO, false).to_value(&challenges),
BlockStackTableRow::new(ONE + HASH_CYCLE_LEN_FELT, ZERO, false).to_value(&challenges),
];
assert_eq!(ONE, p1[0]);
let expected_value = row_values[0];
assert_eq!(expected_value, p1[1]);
for i in 2..10 {
assert_eq!(expected_value, p1[i]);
}
let expected_value = expected_value * row_values[0].inverse() * row_values[1];
assert_eq!(expected_value, p1[10]);
for i in 11..22 {
assert_eq!(expected_value, p1[i]);
}
let expected_value = expected_value * row_values[1].inverse();
assert_eq!(expected_value, ONE);
for i in 22..(p1.len()) {
assert_eq!(ONE, p1[i]);
}
}
#[test]
#[expect(clippy::needless_range_loop)]
fn decoder_p1_join() {
let program = {
let mut mast_forest = MastForest::new();
let basic_block_1_id = BasicBlockNodeBuilder::new(vec![Operation::Mul], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let basic_block_2_id = BasicBlockNodeBuilder::new(vec![Operation::Add], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let join_id = JoinNodeBuilder::new([basic_block_1_id, basic_block_2_id])
.add_to_forest(&mut mast_forest)
.unwrap();
mast_forest.make_root(join_id);
Program::new(mast_forest.into(), join_id)
};
let trace = build_trace_from_program(&program, &[]);
let challenges = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_columns = trace.build_aux_trace(&challenges).unwrap();
let p1 = aux_columns.get_column(P1_COL_IDX);
let challenges = Challenges::<Felt>::new(challenges[0], challenges[1]);
let a_33 = ONE + HASH_CYCLE_LEN_FELT;
let a_65 = a_33 + HASH_CYCLE_LEN_FELT;
let row_values = [
BlockStackTableRow::new(ONE, ZERO, false).to_value(&challenges),
BlockStackTableRow::new(a_33, ONE, false).to_value(&challenges),
BlockStackTableRow::new(a_65, ONE, false).to_value(&challenges),
];
assert_eq!(ONE, p1[0]);
let mut expected_value = row_values[0];
assert_eq!(expected_value, p1[1]);
expected_value *= row_values[1];
assert_eq!(expected_value, p1[2]);
assert_eq!(expected_value, p1[3]);
expected_value *= row_values[1].inverse();
assert_eq!(expected_value, p1[4]);
expected_value *= row_values[2];
assert_eq!(expected_value, p1[5]);
assert_eq!(expected_value, p1[6]);
expected_value *= row_values[2].inverse();
assert_eq!(expected_value, p1[7]);
expected_value *= row_values[0].inverse();
assert_eq!(expected_value, p1[8]);
assert_eq!(expected_value, ONE);
for i in 9..(p1.len()) {
assert_eq!(ONE, p1[i]);
}
}
#[test]
#[expect(clippy::needless_range_loop)]
fn decoder_p1_split() {
let program = {
let mut mast_forest = MastForest::new();
let basic_block_1_id = BasicBlockNodeBuilder::new(vec![Operation::Mul], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let basic_block_2_id = BasicBlockNodeBuilder::new(vec![Operation::Add], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let split_id = SplitNodeBuilder::new([basic_block_1_id, basic_block_2_id])
.add_to_forest(&mut mast_forest)
.unwrap();
mast_forest.make_root(split_id);
Program::new(mast_forest.into(), split_id)
};
let trace = build_trace_from_program(&program, &[1]);
let challenges = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_columns = trace.build_aux_trace(&challenges).unwrap();
let p1 = aux_columns.get_column(P1_COL_IDX);
let challenges = Challenges::<Felt>::new(challenges[0], challenges[1]);
let a_33 = ONE + HASH_CYCLE_LEN_FELT;
let row_values = [
BlockStackTableRow::new(ONE, ZERO, false).to_value(&challenges),
BlockStackTableRow::new(a_33, ONE, false).to_value(&challenges),
];
assert_eq!(ONE, p1[0]);
let mut expected_value = row_values[0];
assert_eq!(expected_value, p1[1]);
expected_value *= row_values[1];
assert_eq!(expected_value, p1[2]);
assert_eq!(expected_value, p1[3]);
expected_value *= row_values[1].inverse();
assert_eq!(expected_value, p1[4]);
expected_value *= row_values[0].inverse();
assert_eq!(expected_value, p1[5]);
assert_eq!(expected_value, ONE);
for i in 6..(p1.len()) {
assert_eq!(ONE, p1[i]);
}
}
#[test]
#[expect(clippy::needless_range_loop)]
fn decoder_p1_loop_with_repeat() {
let program = {
let mut mast_forest = MastForest::new();
let basic_block_1_id = BasicBlockNodeBuilder::new(vec![Operation::Pad], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let basic_block_2_id = BasicBlockNodeBuilder::new(vec![Operation::Drop], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let join_id = JoinNodeBuilder::new([basic_block_1_id, basic_block_2_id])
.add_to_forest(&mut mast_forest)
.unwrap();
let loop_node_id = LoopNodeBuilder::new(join_id).add_to_forest(&mut mast_forest).unwrap();
mast_forest.make_root(loop_node_id);
Program::new(mast_forest.into(), loop_node_id)
};
let trace = build_trace_from_program(&program, &[1, 1, 0]);
let challenges = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_columns = trace.build_aux_trace(&challenges).unwrap();
let p1 = aux_columns.get_column(P1_COL_IDX);
let challenges = Challenges::<Felt>::new(challenges[0], challenges[1]);
let a_33 = ONE + HASH_CYCLE_LEN_FELT; let a_65 = a_33 + HASH_CYCLE_LEN_FELT; let a_97 = a_65 + HASH_CYCLE_LEN_FELT; let a_129 = a_97 + HASH_CYCLE_LEN_FELT; let a_161 = a_129 + HASH_CYCLE_LEN_FELT; let a_193 = a_161 + HASH_CYCLE_LEN_FELT; let row_values = [
BlockStackTableRow::new(ONE, ZERO, true).to_value(&challenges),
BlockStackTableRow::new(a_33, ONE, false).to_value(&challenges),
BlockStackTableRow::new(a_65, a_33, false).to_value(&challenges),
BlockStackTableRow::new(a_97, a_33, false).to_value(&challenges),
BlockStackTableRow::new(a_129, ONE, false).to_value(&challenges),
BlockStackTableRow::new(a_161, a_129, false).to_value(&challenges),
BlockStackTableRow::new(a_193, a_129, false).to_value(&challenges),
];
assert_eq!(ONE, p1[0]);
let mut expected_value = row_values[0];
assert_eq!(expected_value, p1[1]);
expected_value *= row_values[1];
assert_eq!(expected_value, p1[2]);
expected_value *= row_values[2];
assert_eq!(expected_value, p1[3]);
assert_eq!(expected_value, p1[4]);
expected_value *= row_values[2].inverse();
assert_eq!(expected_value, p1[5]);
expected_value *= row_values[3];
assert_eq!(expected_value, p1[6]);
assert_eq!(expected_value, p1[7]);
expected_value *= row_values[3].inverse();
assert_eq!(expected_value, p1[8]);
expected_value *= row_values[1].inverse();
assert_eq!(expected_value, p1[9]);
assert_eq!(expected_value, p1[10]);
expected_value *= row_values[4];
assert_eq!(expected_value, p1[11]);
expected_value *= row_values[5];
assert_eq!(expected_value, p1[12]);
assert_eq!(expected_value, p1[13]);
expected_value *= row_values[5].inverse();
assert_eq!(expected_value, p1[14]);
expected_value *= row_values[6];
assert_eq!(expected_value, p1[15]);
assert_eq!(expected_value, p1[16]);
expected_value *= row_values[6].inverse();
assert_eq!(expected_value, p1[17]);
expected_value *= row_values[4].inverse();
assert_eq!(expected_value, p1[18]);
expected_value *= row_values[0].inverse();
assert_eq!(expected_value, p1[19]);
assert_eq!(expected_value, ONE);
for i in 20..(p1.len()) {
assert_eq!(ONE, p1[i]);
}
}
#[test]
#[expect(clippy::needless_range_loop)]
fn decoder_p2_span_with_respan() {
let program = {
let mut mast_forest = MastForest::new();
let (ops, _) = build_span_with_respan_ops();
let basic_block_id = BasicBlockNodeBuilder::new(ops, Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
mast_forest.make_root(basic_block_id);
Program::new(mast_forest.into(), basic_block_id)
};
let trace = build_trace_from_program(&program, &[]);
let challenges = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_columns = trace.build_aux_trace(&challenges).unwrap();
let p2 = aux_columns.get_column(P2_COL_IDX);
let challenges = Challenges::<Felt>::new(challenges[0], challenges[1]);
let program_hash_msg =
BlockHashTableRow::new_test(ZERO, program.hash(), false, false).collapse(&challenges);
let mut expected_value = ONE;
assert_eq!(expected_value, p2[0]);
for i in 1..22 {
assert_eq!(expected_value, p2[i]);
}
expected_value *= program_hash_msg.inverse();
for i in 22..(p2.len()) {
assert_eq!(expected_value, p2[i]);
}
}
#[test]
#[expect(clippy::needless_range_loop)]
fn decoder_p2_join() {
let mut mast_forest = MastForest::new();
let basic_block_1_id = BasicBlockNodeBuilder::new(vec![Operation::Mul], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let basic_block_2_id = BasicBlockNodeBuilder::new(vec![Operation::Add], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let join_id = JoinNodeBuilder::new([basic_block_1_id, basic_block_2_id])
.add_to_forest(&mut mast_forest)
.unwrap();
let basic_block_1 = mast_forest[basic_block_1_id].clone();
let basic_block_2 = mast_forest[basic_block_2_id].clone();
let join = mast_forest[join_id].clone();
mast_forest.make_root(join_id);
let program = Program::new(mast_forest.into(), join_id);
let trace = build_trace_from_program(&program, &[]);
let challenges = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_columns = trace.build_aux_trace(&challenges).unwrap();
let p2 = aux_columns.get_column(P2_COL_IDX);
let challenges = Challenges::<Felt>::new(challenges[0], challenges[1]);
let program_hash_msg =
BlockHashTableRow::new_test(ZERO, join.digest(), false, false).collapse(&challenges);
let child1_msg =
BlockHashTableRow::new_test(ONE, basic_block_1.digest(), true, false).collapse(&challenges);
let child2_msg = BlockHashTableRow::new_test(ONE, basic_block_2.digest(), false, false)
.collapse(&challenges);
let mut expected_value = ONE;
assert_eq!(expected_value, p2[0]);
expected_value *= child1_msg * child2_msg;
assert_eq!(expected_value, p2[1]);
assert_eq!(expected_value, p2[2]);
assert_eq!(expected_value, p2[3]);
expected_value *= child1_msg.inverse();
assert_eq!(expected_value, p2[4]);
assert_eq!(expected_value, p2[5]);
assert_eq!(expected_value, p2[6]);
expected_value *= child2_msg.inverse();
assert_eq!(expected_value, p2[7]);
expected_value *= program_hash_msg.inverse();
assert_eq!(expected_value, p2[8]);
for i in 9..(p2.len()) {
assert_eq!(expected_value, p2[i]);
}
}
#[test]
#[expect(clippy::needless_range_loop)]
fn decoder_p2_split_true() {
let mut mast_forest = MastForest::new();
let basic_block_1_id = BasicBlockNodeBuilder::new(vec![Operation::Mul], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let basic_block_2_id = BasicBlockNodeBuilder::new(vec![Operation::Add], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let basic_block_1 = mast_forest[basic_block_1_id].clone();
let _basic_block_2 = mast_forest[basic_block_2_id].clone();
let split_id = SplitNodeBuilder::new([basic_block_1_id, basic_block_2_id])
.add_to_forest(&mut mast_forest)
.unwrap();
mast_forest.make_root(split_id);
let program = Program::new(mast_forest.into(), split_id);
let trace = build_trace_from_program(&program, &[1]);
let challenges = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_columns = trace.build_aux_trace(&challenges).unwrap();
let p2 = aux_columns.get_column(P2_COL_IDX);
let challenges = Challenges::<Felt>::new(challenges[0], challenges[1]);
let program_hash_msg =
BlockHashTableRow::new_test(ZERO, program.hash(), false, false).collapse(&challenges);
let child_msg = BlockHashTableRow::new_test(ONE, basic_block_1.digest(), false, false)
.collapse(&challenges);
let mut expected_value = ONE;
assert_eq!(expected_value, p2[0]);
expected_value *= child_msg;
assert_eq!(expected_value, p2[1]);
assert_eq!(expected_value, p2[2]);
assert_eq!(expected_value, p2[3]);
expected_value *= child_msg.inverse();
assert_eq!(expected_value, p2[4]);
expected_value *= program_hash_msg.inverse();
assert_eq!(expected_value, p2[5]);
for i in 6..(p2.len()) {
assert_eq!(expected_value, p2[i]);
}
}
#[test]
#[expect(clippy::needless_range_loop)]
fn decoder_p2_split_false() {
let mut mast_forest = MastForest::new();
let basic_block_1_id = BasicBlockNodeBuilder::new(vec![Operation::Mul], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let basic_block_2_id = BasicBlockNodeBuilder::new(vec![Operation::Add], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let _basic_block_1 = mast_forest[basic_block_1_id].clone();
let basic_block_2 = mast_forest[basic_block_2_id].clone();
let split_id = SplitNodeBuilder::new([basic_block_1_id, basic_block_2_id])
.add_to_forest(&mut mast_forest)
.unwrap();
mast_forest.make_root(split_id);
let program = Program::new(mast_forest.into(), split_id);
let trace = build_trace_from_program(&program, &[0]);
let challenges = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_columns = trace.build_aux_trace(&challenges).unwrap();
let p2 = aux_columns.get_column(P2_COL_IDX);
let challenges = Challenges::<Felt>::new(challenges[0], challenges[1]);
let program_hash_msg =
BlockHashTableRow::new_test(ZERO, program.hash(), false, false).collapse(&challenges);
let child_msg = BlockHashTableRow::new_test(ONE, basic_block_2.digest(), false, false)
.collapse(&challenges);
let mut expected_value = ONE;
assert_eq!(expected_value, p2[0]);
expected_value *= child_msg;
assert_eq!(expected_value, p2[1]);
assert_eq!(expected_value, p2[2]);
assert_eq!(expected_value, p2[3]);
expected_value *= child_msg.inverse();
assert_eq!(expected_value, p2[4]);
expected_value *= program_hash_msg.inverse();
assert_eq!(expected_value, p2[5]);
for i in 6..(p2.len()) {
assert_eq!(expected_value, p2[i]);
}
}
#[test]
#[expect(clippy::needless_range_loop)]
fn decoder_p2_loop_with_repeat() {
let mut mast_forest = MastForest::new();
let basic_block_1_id = BasicBlockNodeBuilder::new(vec![Operation::Pad], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let basic_block_2_id = BasicBlockNodeBuilder::new(vec![Operation::Drop], Vec::new())
.add_to_forest(&mut mast_forest)
.unwrap();
let join_id = JoinNodeBuilder::new([basic_block_1_id, basic_block_2_id])
.add_to_forest(&mut mast_forest)
.unwrap();
let basic_block_1 = mast_forest[basic_block_1_id].clone();
let basic_block_2 = mast_forest[basic_block_2_id].clone();
let join = mast_forest[join_id].clone();
let loop_node_id = LoopNodeBuilder::new(join_id).add_to_forest(&mut mast_forest).unwrap();
mast_forest.make_root(loop_node_id);
let program = Program::new(mast_forest.into(), loop_node_id);
let trace = build_trace_from_program(&program, &[1, 1, 0]);
let challenges = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_columns = trace.build_aux_trace(&challenges).unwrap();
let p2 = aux_columns.get_column(P2_COL_IDX);
let challenges = Challenges::<Felt>::new(challenges[0], challenges[1]);
let a_33 = ONE + HASH_CYCLE_LEN_FELT; let a_129 = a_33 + HASH_CYCLE_LEN_FELT * Felt::new(3); let program_hash_msg =
BlockHashTableRow::new_test(ZERO, program.hash(), false, false).collapse(&challenges);
let loop_body_msg =
BlockHashTableRow::new_test(ONE, join.digest(), false, true).collapse(&challenges);
let child1_iter1 = BlockHashTableRow::new_test(a_33, basic_block_1.digest(), true, false)
.collapse(&challenges);
let child2_iter1 = BlockHashTableRow::new_test(a_33, basic_block_2.digest(), false, false)
.collapse(&challenges);
let child1_iter2 = BlockHashTableRow::new_test(a_129, basic_block_1.digest(), true, false)
.collapse(&challenges);
let child2_iter2 = BlockHashTableRow::new_test(a_129, basic_block_2.digest(), false, false)
.collapse(&challenges);
let mut expected_value = ONE;
assert_eq!(expected_value, p2[0]);
expected_value *= loop_body_msg;
assert_eq!(expected_value, p2[1]);
expected_value *= child1_iter1 * child2_iter1;
assert_eq!(expected_value, p2[2]);
assert_eq!(expected_value, p2[3]);
assert_eq!(expected_value, p2[4]);
expected_value *= child1_iter1.inverse();
assert_eq!(expected_value, p2[5]);
assert_eq!(expected_value, p2[6]);
assert_eq!(expected_value, p2[7]);
expected_value *= child2_iter1.inverse();
assert_eq!(expected_value, p2[8]);
expected_value *= loop_body_msg.inverse();
assert_eq!(expected_value, p2[9]);
expected_value *= loop_body_msg;
assert_eq!(expected_value, p2[10]);
expected_value *= child1_iter2 * child2_iter2;
assert_eq!(expected_value, p2[11]);
assert_eq!(expected_value, p2[12]);
assert_eq!(expected_value, p2[13]);
expected_value *= child1_iter2.inverse();
assert_eq!(expected_value, p2[14]);
assert_eq!(expected_value, p2[15]);
assert_eq!(expected_value, p2[16]);
expected_value *= child2_iter2.inverse();
assert_eq!(expected_value, p2[17]);
expected_value *= loop_body_msg.inverse();
assert_eq!(expected_value, p2[18]);
expected_value *= program_hash_msg.inverse();
assert_eq!(expected_value, p2[19]);
for i in 20..(p2.len()) {
assert_eq!(expected_value, p2[i]);
}
}
#[test]
fn decoder_p3_trace_empty_table() {
let stack = [1, 2];
let operations = vec![Operation::Add];
let trace = build_trace_from_ops(operations, &stack);
let challenges = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_columns = trace.build_aux_trace(&challenges).unwrap();
let p3 = aux_columns.get_column(P3_COL_IDX);
for &value in p3.iter().take(p3.len()) {
assert_eq!(ONE, value);
}
}
#[test]
#[expect(clippy::needless_range_loop)]
fn decoder_p3_trace_one_batch() {
let stack = [1, 2, 3, 4, 5, 6, 7, 8];
let ops = vec![
Operation::Add,
Operation::Mul,
Operation::Add,
Operation::Push(ONE),
Operation::Add,
Operation::Mul,
Operation::Add,
Operation::Push(Felt::new(2)),
Operation::Add,
Operation::Swap,
Operation::Mul,
Operation::Add,
];
let trace = build_trace_from_ops(ops.clone(), &stack);
let challenges = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_columns = trace.build_aux_trace(&challenges).unwrap();
let p3 = aux_columns.get_column(P3_COL_IDX);
let challenges = Challenges::<Felt>::new(challenges[0], challenges[1]);
assert_eq!(ONE, p3[0]);
let g1_value = OpGroupTableRow::new(ONE, Felt::new(3), ONE).to_value(&challenges);
let g2_value = OpGroupTableRow::new(ONE, Felt::new(2), Felt::new(2)).to_value(&challenges);
let g3_value = OpGroupTableRow::new(ONE, ONE, build_op_group(&ops[9..])).to_value(&challenges);
let expected_value = g1_value * g2_value * g3_value;
assert_eq!(expected_value, p3[1]);
for i in 2..5 {
assert_eq!(expected_value, p3[i]);
}
let expected_value = expected_value / g1_value;
assert_eq!(expected_value, p3[5]);
for i in 6..9 {
assert_eq!(expected_value, p3[i]);
}
let expected_value = expected_value / g2_value;
assert_eq!(expected_value, p3[9]);
let expected_value = expected_value / g3_value;
assert_eq!(expected_value, p3[10]);
assert_eq!(expected_value, ONE);
for i in 11..(p3.len()) {
assert_eq!(ONE, p3[i]);
}
}
#[test]
#[expect(clippy::needless_range_loop)]
fn decoder_p3_trace_two_batches() {
let (ops, iv) = build_span_with_respan_ops();
let trace = build_trace_from_ops(ops, &[]);
let challenges = rand_array::<Felt, AUX_TRACE_RAND_CHALLENGES>();
let aux_columns = trace.build_aux_trace(&challenges).unwrap();
let p3 = aux_columns.get_column(P3_COL_IDX);
let challenges = Challenges::<Felt>::new(challenges[0], challenges[1]);
assert_eq!(ONE, p3[0]);
let b0_values = [
OpGroupTableRow::new(ONE, Felt::new(11), iv[0]).to_value(&challenges),
OpGroupTableRow::new(ONE, Felt::new(10), iv[1]).to_value(&challenges),
OpGroupTableRow::new(ONE, Felt::new(9), iv[2]).to_value(&challenges),
OpGroupTableRow::new(ONE, Felt::new(8), iv[3]).to_value(&challenges),
OpGroupTableRow::new(ONE, Felt::new(7), iv[4]).to_value(&challenges),
OpGroupTableRow::new(ONE, Felt::new(6), iv[5]).to_value(&challenges),
OpGroupTableRow::new(ONE, Felt::new(5), iv[6]).to_value(&challenges),
];
let mut expected_value: Felt = b0_values.iter().fold(ONE, |acc, &val| acc * val);
assert_eq!(expected_value, p3[1]);
for (i, clk) in (2..9).enumerate() {
expected_value /= b0_values[i];
assert_eq!(expected_value, p3[clk]);
}
assert_eq!(expected_value, p3[9]);
assert_eq!(expected_value, ONE);
let batch1_addr = ONE + HASH_CYCLE_LEN_FELT;
let op_group3 = build_op_group(&[Operation::Drop; 2]);
let b1_values = [
OpGroupTableRow::new(batch1_addr, Felt::new(3), iv[7]).to_value(&challenges),
OpGroupTableRow::new(batch1_addr, Felt::new(2), iv[8]).to_value(&challenges),
OpGroupTableRow::new(batch1_addr, ONE, op_group3).to_value(&challenges),
];
let mut expected_value: Felt = b1_values.iter().fold(ONE, |acc, &val| acc * val);
assert_eq!(expected_value, p3[10]);
for (i, clk) in (11..13).enumerate() {
expected_value *= b1_values[i].inverse();
assert_eq!(expected_value, p3[clk]);
}
for i in 13..19 {
assert_eq!(expected_value, p3[i]);
}
expected_value *= b1_values[2].inverse();
assert_eq!(expected_value, p3[19]);
assert_eq!(expected_value, ONE);
for i in 20..(p3.len()) {
assert_eq!(ONE, p3[i]);
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct BlockStackTableRow {
block_id: Felt,
parent_id: Felt,
is_loop: bool,
parent_ctx: ContextId,
parent_fn_hash: Word,
parent_fmp: Felt,
parent_stack_depth: u32,
parent_next_overflow_addr: Felt,
}
impl BlockStackTableRow {
#[cfg(test)]
pub fn new(block_id: Felt, parent_id: Felt, is_loop: bool) -> Self {
Self {
block_id,
parent_id,
is_loop,
parent_ctx: ContextId::root(),
parent_fn_hash: miden_core::EMPTY_WORD,
parent_fmp: ZERO,
parent_stack_depth: 0,
parent_next_overflow_addr: ZERO,
}
}
}
impl BlockStackTableRow {
pub fn to_value<E: ExtensionField<Felt>>(&self, challenges: &Challenges<E>) -> E {
let is_loop = if self.is_loop { ONE } else { ZERO };
challenges.alpha
+ challenges.beta_powers[0] * self.block_id
+ challenges.beta_powers[1] * self.parent_id
+ challenges.beta_powers[2] * is_loop
+ challenges.beta_powers[3] * Felt::from_u32(u32::from(self.parent_ctx))
+ challenges.beta_powers[4] * self.parent_fmp
+ challenges.beta_powers[5] * Felt::from_u32(self.parent_stack_depth)
+ challenges.beta_powers[6] * self.parent_next_overflow_addr
+ challenges.beta_powers[7] * self.parent_fn_hash[0]
+ challenges.beta_powers[8] * self.parent_fn_hash[1]
+ challenges.beta_powers[9] * self.parent_fn_hash[2]
+ challenges.beta_powers[10] * self.parent_fn_hash[3]
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct OpGroupTableRow {
batch_id: Felt,
group_pos: Felt,
group_value: Felt,
}
impl OpGroupTableRow {
pub fn new(batch_id: Felt, group_pos: Felt, group_value: Felt) -> Self {
Self { batch_id, group_pos, group_value }
}
}
impl OpGroupTableRow {
pub fn to_value<E: ExtensionField<Felt>>(&self, challenges: &Challenges<E>) -> E {
challenges.alpha
+ challenges.beta_powers[0] * self.batch_id
+ challenges.beta_powers[1] * self.group_pos
+ challenges.beta_powers[2] * self.group_value
}
}