use super::{Felt, FieldElement, StackInputs, StackOutputs, ONE, STACK_TRACE_WIDTH, ZERO};
use alloc::vec::Vec;
use core::cmp;
use vm_core::{stack::STACK_TOP_SIZE, Word, WORD_SIZE};
mod trace;
use trace::StackTrace;
mod overflow;
use overflow::OverflowTable;
pub use overflow::OverflowTableRow;
mod aux_trace;
pub use aux_trace::AuxTraceBuilder;
#[cfg(test)]
mod tests;
const MAX_TOP_IDX: usize = STACK_TOP_SIZE - 1;
pub struct Stack {
clk: u32,
trace: StackTrace,
overflow: OverflowTable,
active_depth: usize,
full_depth: usize,
}
impl Stack {
pub fn new(
inputs: &StackInputs,
init_trace_capacity: usize,
keep_overflow_trace: bool,
) -> Self {
let init_values = inputs.values();
let depth = cmp::max(STACK_TOP_SIZE, init_values.len());
let (trace, overflow) = if init_values.len() > STACK_TOP_SIZE {
let overflow =
OverflowTable::new_with_inputs(keep_overflow_trace, &init_values[STACK_TOP_SIZE..]);
let trace =
StackTrace::new(&init_values[..STACK_TOP_SIZE], init_trace_capacity, depth, -ONE);
(trace, overflow)
} else {
let overflow = OverflowTable::new(keep_overflow_trace);
let trace = StackTrace::new(init_values, init_trace_capacity, depth, ZERO);
(trace, overflow)
};
Self {
clk: 0,
trace,
overflow,
active_depth: depth,
full_depth: depth,
}
}
pub fn depth(&self) -> usize {
self.active_depth
}
pub fn current_clk(&self) -> u32 {
self.clk
}
pub fn trace_len(&self) -> usize {
self.clk as usize
}
pub fn peek(&self) -> Felt {
self.trace.peek_at(self.clk)
}
pub fn get_state_at(&self, clk: u32) -> Vec<Felt> {
let mut result = Vec::with_capacity(self.active_depth);
self.trace.append_state_into(&mut result, clk);
if clk == self.clk {
self.overflow.append_into(&mut result);
} else {
self.overflow.append_state_into(&mut result, clk as u64);
}
result
}
pub fn build_stack_outputs(&self) -> StackOutputs {
let mut stack_items = Vec::with_capacity(self.active_depth);
self.trace.append_state_into(&mut stack_items, self.clk);
self.overflow.append_into(&mut stack_items);
StackOutputs::new(stack_items, self.overflow.get_addrs())
.expect("processor stack handling logic is valid")
}
pub fn get(&self, pos: usize) -> Felt {
debug_assert!(pos < STACK_TOP_SIZE, "stack underflow");
self.trace.get_stack_value_at(self.clk, pos)
}
pub fn get_word(&self, word_idx: usize) -> Word {
let offset = word_idx * WORD_SIZE;
[
self.get(offset + 3),
self.get(offset + 2),
self.get(offset + 1),
self.get(offset),
]
}
pub fn set(&mut self, pos: usize, value: Felt) {
debug_assert!(pos < STACK_TOP_SIZE, "stack underflow");
self.trace.set_stack_value_at(self.clk + 1, pos, value);
}
pub fn copy_state(&mut self, start_pos: usize) {
self.trace.copy_stack_state_at(
self.clk,
start_pos,
Felt::try_from(self.active_depth as u64)
.expect("value is greater than or equal to the field modulus"),
self.overflow.last_row_addr(),
);
}
pub fn shift_left(&mut self, start_pos: usize) {
debug_assert!(start_pos > 0, "start position must be greater than 0");
debug_assert!(start_pos <= STACK_TOP_SIZE, "start position cannot exceed stack top size");
match self.active_depth {
0..=MAX_TOP_IDX => unreachable!("stack underflow"),
STACK_TOP_SIZE => {
self.trace.stack_shift_left_at(self.clk, start_pos, ZERO, None);
}
_ => {
let from_overflow = self.overflow.pop(self.clk as u64);
self.trace.stack_shift_left_at(
self.clk,
start_pos,
from_overflow,
Some(self.overflow.last_row_addr()),
);
self.active_depth -= 1;
self.full_depth -= 1;
}
}
}
pub fn shift_right(&mut self, start_pos: usize) {
debug_assert!(start_pos < STACK_TOP_SIZE, "start position cannot exceed stack top size");
self.trace.stack_shift_right_at(self.clk, start_pos);
let to_overflow = self.trace.get_stack_value_at(self.clk, MAX_TOP_IDX);
self.overflow.push(to_overflow, Felt::from(self.clk));
self.active_depth += 1;
self.full_depth += 1;
}
pub fn start_context(&mut self) -> (usize, Felt) {
let current_depth = self.active_depth;
let current_overflow_addr = self.overflow.last_row_addr();
self.active_depth = STACK_TOP_SIZE;
self.overflow.set_last_row_addr(ZERO);
(current_depth, current_overflow_addr)
}
pub fn restore_context(&mut self, stack_depth: usize, next_overflow_addr: Felt) {
debug_assert!(stack_depth <= self.full_depth, "stack depth too big");
debug_assert_eq!(self.active_depth, STACK_TOP_SIZE, "overflow table not empty");
self.active_depth = stack_depth;
self.overflow.set_last_row_addr(next_overflow_addr);
}
pub fn into_trace(self, trace_len: usize, num_rand_rows: usize) -> super::StackTrace {
let clk = self.current_clk() as usize;
assert!(clk + num_rand_rows <= trace_len, "target trace length too small");
assert_eq!(self.active_depth, self.full_depth, "inconsistent stack depth");
let mut trace = self.trace.into_array();
for column in trace.iter_mut() {
let last_value = column[clk];
column[clk..].fill(last_value);
column.resize(trace_len, last_value);
}
super::StackTrace {
trace,
aux_builder: self.overflow.into_aux_builder(),
}
}
pub fn ensure_trace_capacity(&mut self) {
self.trace.ensure_trace_capacity(self.clk);
}
pub fn advance_clock(&mut self) {
self.clk += 1;
}
#[cfg(any(test, feature = "internals"))]
pub fn trace_state(&self) -> [Felt; STACK_TOP_SIZE] {
self.trace.get_stack_state_at(self.clk)
}
#[cfg(test)]
pub fn helpers_state(&self) -> [Felt; miden_air::trace::stack::NUM_STACK_HELPER_COLS] {
self.trace.get_helpers_state_at(self.clk)
}
}