use core::array;
use miden_core::field::PrimeCharacteristicRing;
use crate::{
constraints::{
lookup::{
main_air::{MainBusContext, MainLookupBuilder},
messages::{BlockHashMsg, OpGroupMsg},
},
utils::{BoolNot, horner_eval_bits},
},
lookup::{Deg, LookupBatch, LookupColumn, LookupGroup},
};
pub(in crate::constraints::lookup) const MAX_INTERACTIONS_PER_ROW: usize = 7;
#[allow(clippy::too_many_lines)]
pub(in crate::constraints::lookup) fn emit_block_hash_and_op_group<LB>(
builder: &mut LB,
ctx: &MainBusContext<LB>,
) where
LB: MainLookupBuilder,
{
let local = ctx.local;
let next = ctx.next;
let op_flags = &ctx.op_flags;
let dec = &local.decoder;
let dec_next = &next.decoder;
let stk = &local.stack;
let stk_next = &next.stack;
let addr = dec.addr;
let addr_next = dec_next.addr;
let h_0: [LB::Var; 4] = array::from_fn(|i| dec.hasher_state[i]);
let h_1: [LB::Var; 4] = array::from_fn(|i| dec.hasher_state[4 + i]);
let s0 = stk.get(0);
let f_join = op_flags.join();
let f_split = op_flags.split();
let f_loop_body = op_flags.loop_op() * s0 + op_flags.repeat();
let f_child = op_flags.dyn_op() + op_flags.dyncall() + op_flags.call() + op_flags.syscall();
let f_end = op_flags.end();
let f_push = op_flags.push();
let c0: LB::Expr = dec.batch_flags[0].into();
let c1: LB::Expr = dec.batch_flags[1].into();
let c2: LB::Expr = dec.batch_flags[2].into();
let gc: LB::Expr = dec.group_count.into();
let in_span: LB::Expr = dec.in_span.into();
let gc_next: LB::Expr = dec_next.group_count.into();
let f_rem = in_span * (gc.clone() - gc_next);
builder.next_column(
|col| {
col.group(
"merged_interactions",
|g| {
g.batch(
"join",
f_join,
|b| {
let parent: LB::Expr = addr_next.into();
let first_hash = h_0.map(LB::Expr::from);
let second_hash = h_1.map(LB::Expr::from);
b.add(
"join_first_child",
BlockHashMsg::FirstChild {
parent: parent.clone(),
child_hash: first_hash,
},
Deg { v: 5, u: 6 },
);
b.add(
"join_second_child",
BlockHashMsg::Child { parent, child_hash: second_hash },
Deg { v: 5, u: 6 },
);
},
Deg { v: 6, u: 7 }, );
g.add(
"split",
f_split,
|| {
let parent = addr_next.into();
let s0: LB::Expr = s0.into();
let one_minus_s0 = s0.not();
let child_hash = array::from_fn(|i| {
s0.clone() * h_0[i].into() + one_minus_s0.clone() * h_1[i].into()
});
BlockHashMsg::Child { parent, child_hash }
},
Deg { v: 5, u: 7 },
);
g.add(
"loop_repeat",
f_loop_body,
|| {
let parent = addr_next.into();
let child_hash = h_0.map(LB::Expr::from);
BlockHashMsg::LoopBody { parent, child_hash }
},
Deg { v: 6, u: 7 },
);
g.add(
"dyn_dyncall_call_syscall",
f_child,
|| {
let parent = addr_next.into();
let child_hash = h_0.map(LB::Expr::from);
BlockHashMsg::Child { parent, child_hash }
},
Deg { v: 5, u: 6 },
);
g.remove(
"end",
f_end,
|| {
let parent = addr_next.into();
let child_hash = h_0.map(LB::Expr::from);
let is_first_child = LB::Expr::ONE
- op_flags.end_next()
- op_flags.repeat_next()
- op_flags.respan_next()
- op_flags.halt_next();
let is_loop_body = dec.end_block_flags().is_loop_body.into();
BlockHashMsg::End {
parent,
child_hash,
is_first_child,
is_loop_body,
}
},
Deg { v: 4, u: 8 },
);
let gc8 = gc.clone();
g.batch(
"g8_batch",
c0.clone(),
move |b| {
let batch_id: LB::Expr = addr_next.into();
for i in 1u16..=3 {
let group_value = h_0[i as usize].into();
b.add(
"g8_group",
OpGroupMsg::new(&batch_id, gc8.clone(), i, group_value),
Deg { v: 1, u: 2 },
);
}
for i in 4u16..=7 {
let group_value = h_1[(i - 4) as usize].into();
b.add(
"g8_group",
OpGroupMsg::new(&batch_id, gc8.clone(), i, group_value),
Deg { v: 1, u: 2 },
);
}
},
Deg { v: 7, u: 8 }, );
let gc4 = gc.clone();
g.batch(
"g4_batch",
c0.not() * c1.clone() * c2.not(),
move |b| {
let batch_id: LB::Expr = addr_next.into();
for i in 1u16..=3 {
let group_value = h_0[i as usize].into();
b.add(
"g4_group",
OpGroupMsg::new(&batch_id, gc4.clone(), i, group_value),
Deg { v: 3, u: 4 },
);
}
},
Deg { v: 5, u: 6 }, );
let gc2 = gc.clone();
let f_g2 = c0.not() * c1.not() * c2;
g.add(
"g2",
f_g2,
move || {
let batch_id: LB::Expr = addr_next.into();
let group_value = h_0[1].into();
OpGroupMsg::new(&batch_id, gc2, 1, group_value)
},
Deg { v: 3, u: 4 },
);
g.remove(
"op_group_removal",
f_rem,
move || {
let opcode_next = horner_eval_bits::<7, _, LB::Expr>(&dec_next.op_bits);
let stk_next_0: LB::Expr = stk_next.get(0).into();
let h0_next: LB::Expr = dec_next.hasher_state[0].into();
let group_value = f_push.clone() * stk_next_0
+ f_push.not() * (h0_next * LB::Expr::from_u16(128) + opcode_next);
let batch_id = addr.into();
OpGroupMsg { batch_id, group_pos: gc, group_value }
},
Deg { v: 2, u: 8 },
);
},
Deg { v: 7, u: 8 },
);
},
Deg { v: 7, u: 8 },
);
}