use super::{
super::trace::LookupTableRow, get_num_groups_in_next_batch, BlockInfo, Felt, FieldElement,
StarkField, Vec, Word, ONE, ZERO,
};
use crate::Matrix;
pub struct AuxTraceHints {
block_exec_hints: Vec<(u32, BlockTableUpdate)>,
block_stack_rows: Vec<BlockStackTableRow>,
block_hash_rows: Vec<BlockHashTableRow>,
op_group_hints: Vec<(u32, OpGroupTableUpdate)>,
op_group_rows: Vec<OpGroupTableRow>,
}
impl AuxTraceHints {
pub fn new() -> Self {
let block_hash_rows = vec![BlockHashTableRow::from_program_hash([ZERO; 4])];
Self {
block_exec_hints: Vec::new(),
block_stack_rows: Vec::new(),
block_hash_rows,
op_group_hints: Vec::new(),
op_group_rows: Vec::new(),
}
}
pub fn block_exec_hints(&self) -> &[(u32, BlockTableUpdate)] {
&self.block_exec_hints
}
pub fn block_stack_table_rows(&self) -> &[BlockStackTableRow] {
&self.block_stack_rows
}
pub fn block_hash_table_rows(&self) -> &[BlockHashTableRow] {
&self.block_hash_rows
}
pub fn op_group_table_hints(&self) -> &[(u32, OpGroupTableUpdate)] {
&self.op_group_hints
}
pub fn op_group_table_rows(&self) -> &[OpGroupTableRow] {
&self.op_group_rows
}
pub fn get_block_stack_row_idx(&self, block_id: Felt) -> Option<usize> {
let block_id = block_id.as_int();
self.block_stack_rows
.binary_search_by_key(&block_id, |row| row.block_id.as_int())
.ok()
}
pub fn get_block_hash_row_idx(&self, parent_id: Felt, is_first_child: bool) -> Option<usize> {
let parent_id = parent_id.as_int();
match self
.block_hash_rows
.binary_search_by_key(&parent_id, |row| row.parent_id.as_int())
{
Ok(idx) => {
if self.block_hash_rows[idx].is_first_child == is_first_child {
Some(idx)
} else if is_first_child {
let row = &self.block_hash_rows[idx - 1];
debug_assert_eq!(row.parent_id.as_int(), parent_id);
debug_assert_eq!(row.is_first_child, is_first_child);
Some(idx - 1)
} else {
let row = &self.block_hash_rows[idx + 1];
debug_assert_eq!(row.parent_id.as_int(), parent_id);
debug_assert_eq!(row.is_first_child, is_first_child);
Some(idx + 1)
}
}
Err(_) => None,
}
}
pub fn block_started(
&mut self,
clk: u32,
block_info: &BlockInfo,
child1_hash: Option<Word>,
child2_hash: Option<Word>,
) {
let hint = BlockTableUpdate::BlockStarted(block_info.num_children());
self.block_exec_hints.push((clk, hint));
let bst_row = BlockStackTableRow::new(block_info);
self.block_stack_rows.push(bst_row);
if let Some(child1_hash) = child1_hash {
let is_first_child = child2_hash.is_some();
let bsh_row1 = BlockHashTableRow::from_parent(block_info, child1_hash, is_first_child);
self.block_hash_rows.push(bsh_row1);
if let Some(child2_hash) = child2_hash {
let bsh_row2 = BlockHashTableRow::from_parent(block_info, child2_hash, false);
self.block_hash_rows.push(bsh_row2);
}
}
}
pub fn block_ended(&mut self, clk: u32, is_first_child: bool) {
self.block_exec_hints
.push((clk, BlockTableUpdate::BlockEnded(is_first_child)));
}
pub fn loop_repeat_started(&mut self, clk: u32) {
self.block_exec_hints
.push((clk, BlockTableUpdate::LoopRepeated));
}
pub fn span_extended(&mut self, clk: u32, block_info: &BlockInfo) {
let row = BlockStackTableRow::new(block_info);
self.block_stack_rows.push(row);
self.block_exec_hints
.push((clk, BlockTableUpdate::SpanExtended))
}
pub fn insert_op_batch(&mut self, clk: u32, num_groups_left: Felt) {
let num_batch_groups = get_num_groups_in_next_batch(num_groups_left);
debug_assert!(num_batch_groups > 0, "op batch is empty");
let num_inserted_groups = num_batch_groups - 1;
if num_inserted_groups > 0 {
let update = OpGroupTableUpdate::InsertRows(num_inserted_groups as u32);
self.op_group_hints.push((clk, update));
}
}
pub fn remove_op_group(
&mut self,
clk: u32,
batch_id: Felt,
group_pos: Felt,
group_value: Felt,
) {
self.op_group_hints
.push((clk, OpGroupTableUpdate::RemoveRow));
self.op_group_rows
.push(OpGroupTableRow::new(batch_id, group_pos, group_value));
}
pub fn set_program_hash(&mut self, program_hash: Word) {
self.block_hash_rows[0] = BlockHashTableRow::from_program_hash(program_hash);
}
}
impl Default for AuxTraceHints {
fn default() -> Self {
Self::new()
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum BlockTableUpdate {
BlockStarted(u32), SpanExtended,
LoopRepeated,
BlockEnded(bool), }
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum OpGroupTableUpdate {
InsertRows(u32),
RemoveRow,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BlockStackTableRow {
block_id: Felt,
parent_id: Felt,
is_loop: bool,
parent_ctx: u32,
parent_fn_hash: Word,
parent_fmp: Felt,
parent_stack_depth: u32,
parent_next_overflow_addr: Felt,
}
impl BlockStackTableRow {
pub fn new(block_info: &BlockInfo) -> Self {
let ctx_info = block_info.ctx_info.unwrap_or_default();
Self {
block_id: block_info.addr,
parent_id: block_info.parent_addr,
is_loop: block_info.is_entered_loop() == ONE,
parent_ctx: ctx_info.parent_ctx,
parent_fn_hash: ctx_info.parent_fn_hash,
parent_fmp: ctx_info.parent_fmp,
parent_stack_depth: ctx_info.parent_stack_depth,
parent_next_overflow_addr: ctx_info.parent_next_overflow_addr,
}
}
#[cfg(test)]
pub fn new_test(block_id: Felt, parent_id: Felt, is_loop: bool) -> Self {
Self {
block_id,
parent_id,
is_loop,
parent_ctx: 0,
parent_fn_hash: [ZERO; 4],
parent_fmp: ZERO,
parent_stack_depth: 0,
parent_next_overflow_addr: ZERO,
}
}
#[cfg(test)]
pub fn new_test_with_ctx(
block_id: Felt,
parent_id: Felt,
is_loop: bool,
ctx_info: super::ExecutionContextInfo,
) -> Self {
Self {
block_id,
parent_id,
is_loop,
parent_ctx: ctx_info.parent_ctx,
parent_fn_hash: ctx_info.parent_fn_hash,
parent_fmp: ctx_info.parent_fmp,
parent_stack_depth: ctx_info.parent_stack_depth,
parent_next_overflow_addr: ctx_info.parent_next_overflow_addr,
}
}
}
impl LookupTableRow for BlockStackTableRow {
fn to_value<E: FieldElement<BaseField = Felt>>(
&self,
_main_trace: &Matrix<Felt>,
alphas: &[E],
) -> E {
let is_loop = if self.is_loop { ONE } else { ZERO };
alphas[0]
+ alphas[1].mul_base(self.block_id)
+ alphas[2].mul_base(self.parent_id)
+ alphas[3].mul_base(is_loop)
+ alphas[4].mul_base(Felt::from(self.parent_ctx))
+ alphas[5].mul_base(self.parent_fmp)
+ alphas[6].mul_base(Felt::from(self.parent_stack_depth))
+ alphas[7].mul_base(self.parent_next_overflow_addr)
+ alphas[8].mul_base(self.parent_fn_hash[0])
+ alphas[9].mul_base(self.parent_fn_hash[1])
+ alphas[10].mul_base(self.parent_fn_hash[2])
+ alphas[11].mul_base(self.parent_fn_hash[3])
}
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub struct BlockHashTableRow {
parent_id: Felt,
block_hash: Word,
is_first_child: bool,
is_loop_body: bool,
}
impl BlockHashTableRow {
pub fn from_parent(parent_info: &BlockInfo, block_hash: Word, is_first_child: bool) -> Self {
Self {
parent_id: parent_info.addr,
block_hash,
is_first_child,
is_loop_body: parent_info.is_entered_loop() == ONE,
}
}
pub fn from_program_hash(program_hash: Word) -> Self {
Self {
parent_id: ZERO,
block_hash: program_hash,
is_first_child: false,
is_loop_body: false,
}
}
#[cfg(test)]
pub fn new_test(
parent_id: Felt,
block_hash: Word,
is_first_child: bool,
is_loop_body: bool,
) -> Self {
Self {
parent_id,
block_hash,
is_first_child,
is_loop_body,
}
}
pub fn is_first_child(&self) -> bool {
self.is_first_child
}
}
impl LookupTableRow for BlockHashTableRow {
fn to_value<E: FieldElement<BaseField = Felt>>(
&self,
_main_trace: &Matrix<Felt>,
alphas: &[E],
) -> E {
let is_first_child = if self.is_first_child { ONE } else { ZERO };
let is_loop_body = if self.is_loop_body { ONE } else { ZERO };
alphas[0]
+ alphas[1].mul_base(self.parent_id)
+ alphas[2].mul_base(self.block_hash[0])
+ alphas[3].mul_base(self.block_hash[1])
+ alphas[4].mul_base(self.block_hash[2])
+ alphas[5].mul_base(self.block_hash[3])
+ alphas[6].mul_base(is_first_child)
+ alphas[7].mul_base(is_loop_body)
}
}
#[derive(Debug, Copy, 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 LookupTableRow for OpGroupTableRow {
fn to_value<E: FieldElement<BaseField = Felt>>(
&self,
_main_trace: &Matrix<Felt>,
alphas: &[E],
) -> E {
alphas[0]
+ alphas[1].mul_base(self.batch_id)
+ alphas[2].mul_base(self.group_pos)
+ alphas[3].mul_base(self.group_value)
}
}