use alloc::vec::Vec;
use miden_air::RowIndex;
use miden_utils_indexing::IndexVec;
use super::{Felt, ZERO};
#[derive(Debug, Clone, Default)]
struct OverflowStackEntry {
pub value: Felt,
pub clk: RowIndex,
}
impl OverflowStackEntry {
pub fn new(value: Felt, clk: RowIndex) -> Self {
Self { value, clk }
}
pub fn value(&self) -> Felt {
self.value
}
}
#[derive(Debug, Default, Clone)]
struct OverflowStack {
overflow: IndexVec<RowIndex, OverflowStackEntry>,
}
impl OverflowStack {
pub fn new() -> Self {
Self { overflow: IndexVec::new() }
}
pub fn last(&self) -> Option<&OverflowStackEntry> {
self.overflow.as_slice().last()
}
pub fn num_elements(&self) -> usize {
self.overflow.len()
}
pub fn is_empty(&self) -> bool {
self.overflow.is_empty()
}
pub fn iter(&self) -> impl DoubleEndedIterator<Item = &OverflowStackEntry> {
self.overflow.as_slice().iter()
}
pub fn push(&mut self, entry: OverflowStackEntry) {
let _ = self.overflow.push(entry);
}
pub fn pop(&mut self) -> Option<OverflowStackEntry> {
if self.overflow.is_empty() {
None
} else {
Some(self.overflow.swap_remove(self.overflow.len() - 1))
}
}
}
#[derive(Debug, Clone)]
pub struct OverflowTable {
overflow: IndexVec<RowIndex, OverflowStack>,
clk: RowIndex,
history: Option<OverflowTableHistory>,
}
impl OverflowTable {
pub fn new(save_history: bool) -> Self {
let mut overflow = IndexVec::new();
let _ = overflow.push(OverflowStack::new());
Self {
overflow,
clk: RowIndex::from(0),
history: save_history.then(OverflowTableHistory::new),
}
}
pub fn append_into(&self, target: &mut Vec<Felt>) {
let current_overflow_stack = self.get_current_overflow_stack();
target.extend(current_overflow_stack.iter().rev().map(OverflowStackEntry::value));
}
pub fn append_from_history_at(&self, clk: RowIndex, target: &mut Vec<Felt>) {
let history = self.history.as_ref().expect("overflow history not enabled");
let overflow_at_clk = history.get_at(clk);
target.extend(overflow_at_clk);
}
pub fn last_update_clk_in_current_ctx(&self) -> Felt {
self.get_current_overflow_stack()
.last()
.map_or(ZERO, |entry| Felt::from(entry.clk))
}
pub fn total_num_elements(&self) -> usize {
self.overflow.iter().map(OverflowStack::num_elements).sum::<usize>()
}
pub fn get_element_at(&self, index: usize) -> Option<Felt> {
let current_stack = self.get_current_overflow_stack();
let len = current_stack.num_elements();
if index >= len {
None
} else {
let actual_index = RowIndex::from(len - 1 - index);
current_stack.overflow.get(actual_index).map(|entry| entry.value)
}
}
pub fn num_elements_in_current_ctx(&self) -> usize {
self.get_current_overflow_stack().num_elements()
}
pub fn push(&mut self, value: Felt) {
let clk = self.clk;
self.get_current_overflow_stack_mut().push(OverflowStackEntry::new(value, clk));
self.save_stack_to_history();
}
pub fn pop(&mut self) -> Option<Felt> {
let value_popped = self
.get_current_overflow_stack_mut()
.pop()
.as_ref()
.map(OverflowStackEntry::value);
self.save_stack_to_history();
value_popped
}
pub fn start_context(&mut self) {
let _ = self.overflow.push(OverflowStack::new());
self.save_stack_to_history();
}
pub fn restore_context(&mut self) {
let overflow_stack_for_ctx = self.overflow.swap_remove(self.overflow.len() - 1);
assert!(
overflow_stack_for_ctx.is_empty(),
"the overflow stack for the current context should be empty when restoring a context"
);
self.save_stack_to_history();
}
pub fn advance_clock(&mut self) {
self.clk += 1_u32;
}
fn get_current_overflow_stack(&self) -> &OverflowStack {
self.overflow
.as_slice()
.last()
.expect("The current context should always have an overflow stack initialized")
}
fn get_current_overflow_stack_mut(&mut self) -> &mut OverflowStack {
let len = self.overflow.len();
&mut self.overflow[RowIndex::from(len - 1)]
}
fn save_stack_to_history(&mut self) {
let clk = self.clk;
if self.history.is_some() {
let stack_after_op: Vec<Felt> = self
.get_current_overflow_stack()
.iter()
.map(OverflowStackEntry::value)
.collect();
self.history.as_mut().unwrap().save_stack_to_history(clk, stack_after_op);
}
}
}
impl Default for OverflowTable {
fn default() -> Self {
Self::new(false)
}
}
#[derive(Debug, Clone)]
struct OverflowTableHistory {
history: Vec<(RowIndex, Vec<Felt>)>,
}
impl OverflowTableHistory {
pub fn new() -> Self {
let init_overflow = (RowIndex::from(0), vec![]);
Self { history: vec![init_overflow] }
}
pub fn get_at(&self, target_clk: RowIndex) -> impl Iterator<Item = &Felt> {
match self.history.binary_search_by_key(&target_clk, |entry| entry.0) {
Ok(idx) => self.history[idx].1.iter().rev(),
Err(insertion_idx) => {
if insertion_idx > 0 {
self.history[insertion_idx - 1].1.iter().rev()
} else {
[].iter().rev()
}
},
}
}
pub fn save_stack_to_history(&mut self, clk: RowIndex, stack: Vec<Felt>) {
if let Some(last_entry) = self.history.last()
&& last_entry.0 == clk
{
return;
}
self.history.push((clk, stack));
}
}