use super::{AuxTraceBuilder, Felt, FieldElement, ZERO};
use alloc::collections::BTreeMap;
use alloc::vec::Vec;
use vm_core::{utils::uninit_vector, StarkField};
pub struct OverflowTable {
all_rows: Vec<OverflowTableRow>,
active_rows: Vec<usize>,
trace: BTreeMap<u64, Vec<Felt>>,
trace_enabled: bool,
num_init_rows: usize,
last_row_addr: Felt,
}
impl OverflowTable {
pub fn new(enable_trace: bool) -> Self {
Self {
all_rows: Vec::new(),
active_rows: Vec::new(),
trace: BTreeMap::new(),
trace_enabled: enable_trace,
num_init_rows: 0,
last_row_addr: ZERO,
}
}
pub fn new_with_inputs(enable_trace: bool, init_values: &[Felt]) -> Self {
let mut overflow_table = Self::new(enable_trace);
overflow_table.num_init_rows = init_values.len();
let mut clk = Felt::MODULUS - init_values.len() as u64;
for &val in init_values.iter().rev() {
overflow_table.push(val, Felt::new(clk));
clk += 1;
}
overflow_table
}
pub fn push(&mut self, value: Felt, clk: Felt) {
debug_assert_ne!(clk, ZERO, "cannot add value to overflow at clk=0");
let row_idx = self.all_rows.len() as u32;
let new_row = OverflowTableRow::new(clk, value, self.last_row_addr);
self.all_rows.push(new_row);
self.active_rows.push(row_idx as usize);
self.last_row_addr = clk;
if self.trace_enabled {
self.save_current_state(clk.as_int());
}
}
pub fn pop(&mut self, clk: u64) -> Felt {
debug_assert_ne!(
self.last_row_addr, ZERO,
"overflow table is empty in the current context"
);
let last_row_idx = self.active_rows.pop().expect("overflow table is empty");
let last_row = &self.all_rows[last_row_idx];
let removed_value = last_row.val;
self.last_row_addr = last_row.prev;
if self.trace_enabled {
self.save_current_state(clk);
}
removed_value
}
pub fn set_last_row_addr(&mut self, last_row_addr: Felt) {
if last_row_addr != ZERO {
let last_row_idx = *self.active_rows.last().expect("overflow table is empty");
assert_eq!(self.all_rows[last_row_idx].clk, last_row_addr);
}
self.last_row_addr = last_row_addr;
}
pub fn last_row_addr(&self) -> Felt {
self.last_row_addr
}
pub fn append_into(&self, target: &mut Vec<Felt>) {
for &idx in self.active_rows.iter().rev() {
target.push(self.all_rows[idx].val);
}
}
pub fn append_state_into(&self, target: &mut Vec<Felt>, clk: u64) {
assert!(self.trace_enabled, "overflow trace not enabled");
if let Some(x) = self.trace.range(0..=clk).last() {
for item in x.1.iter().rev() {
target.push(*item);
}
}
}
pub(super) fn get_addrs(&self) -> Vec<Felt> {
if self.active_rows.is_empty() {
return Vec::new();
}
let mut addrs = unsafe { uninit_vector(self.active_rows.len() + 1) };
addrs[0] = self.all_rows[self.active_rows[0]].prev;
for (i, &row_idx) in self.active_rows.iter().enumerate() {
addrs[i + 1] = self.all_rows[row_idx].clk;
}
addrs
}
pub fn into_aux_builder(self) -> AuxTraceBuilder {
AuxTraceBuilder {
num_init_rows: self.num_init_rows,
overflow_table_rows: self.all_rows,
}
}
fn save_current_state(&mut self, clk: u64) {
debug_assert!(self.trace_enabled, "overflow table trace not enabled");
let current_state = self.active_rows.iter().map(|&idx| self.all_rows[idx].val).collect();
self.trace.insert(clk, current_state);
}
#[cfg(test)]
pub fn all_rows(&self) -> &[OverflowTableRow] {
&self.all_rows
}
#[cfg(test)]
pub fn active_rows(&self) -> &[usize] {
&self.active_rows
}
}
#[derive(Debug, PartialEq, Eq)]
pub struct OverflowTableRow {
val: Felt,
clk: Felt,
prev: Felt,
}
impl OverflowTableRow {
pub fn new(clk: Felt, val: Felt, prev: Felt) -> Self {
Self { val, clk, prev }
}
}
impl OverflowTableRow {
pub fn to_value<E: FieldElement<BaseField = Felt>>(&self, alphas: &[E]) -> E {
alphas[0]
+ alphas[1].mul_base(self.clk)
+ alphas[2].mul_base(self.val)
+ alphas[3].mul_base(self.prev)
}
}