use alloc::vec::Vec;
use core::fmt::{self, Display};
use miden_air::RowIndex;
use super::{EMPTY_WORD, ExecutionError, Felt, FieldElement, SysTrace, Word, ZERO};
#[cfg(test)]
mod tests;
#[derive(Debug)]
pub struct System {
clk: RowIndex,
ctx: ContextId,
fn_hash: Word,
ctx_trace: Vec<Felt>,
clk_trace: Vec<Felt>,
fn_hash_trace: [Vec<Felt>; 4],
}
impl System {
pub fn new(init_trace_capacity: usize) -> Self {
Self {
clk: RowIndex::from(0),
ctx: ContextId::root(),
fn_hash: EMPTY_WORD,
clk_trace: vec![Felt::ZERO; init_trace_capacity],
ctx_trace: vec![Felt::ZERO; init_trace_capacity],
fn_hash_trace: [
vec![Felt::ZERO; init_trace_capacity],
vec![Felt::ZERO; init_trace_capacity],
vec![Felt::ZERO; init_trace_capacity],
vec![Felt::ZERO; init_trace_capacity],
],
}
}
#[inline(always)]
pub fn clk(&self) -> RowIndex {
self.clk
}
#[inline(always)]
pub fn ctx(&self) -> ContextId {
self.ctx
}
#[inline(always)]
pub fn fn_hash(&self) -> Word {
self.fn_hash
}
#[inline(always)]
pub fn trace_len(&self) -> usize {
self.clk.into()
}
#[inline(always)]
pub fn get_ctx_at(&self, clk: RowIndex) -> ContextId {
(self.ctx_trace[clk.as_usize()].as_int() as u32).into()
}
pub fn advance_clock(&mut self, max_cycles: u32) -> Result<(), ExecutionError> {
self.clk += 1_u32;
if self.clk.as_u32() > max_cycles {
return Err(ExecutionError::CycleLimitExceeded(max_cycles));
}
let clk: usize = self.clk.into();
self.clk_trace[clk] = Felt::from(self.clk);
self.ctx_trace[clk] = Felt::from(self.ctx);
self.fn_hash_trace[0][clk] = self.fn_hash[0];
self.fn_hash_trace[1][clk] = self.fn_hash[1];
self.fn_hash_trace[2][clk] = self.fn_hash[2];
self.fn_hash_trace[3][clk] = self.fn_hash[3];
Ok(())
}
pub fn start_call_or_dyncall(&mut self, fn_hash: Word) {
self.ctx = self.get_next_ctx_id();
self.fn_hash = fn_hash;
}
pub fn start_syscall(&mut self) {
self.ctx = ContextId::root();
}
pub fn restore_context(&mut self, ctx: ContextId, fn_hash: Word) {
self.ctx = ctx;
self.fn_hash = fn_hash;
}
pub fn into_trace(mut self, trace_len: usize, num_rand_rows: usize) -> SysTrace {
let clk: usize = self.clk().into();
assert!(clk + num_rand_rows <= trace_len, "target trace length too small");
self.clk_trace.resize(trace_len, ZERO);
for (i, clk) in self.clk_trace.iter_mut().enumerate().skip(clk) {
*clk = Felt::from(i as u32);
}
debug_assert!(self.ctx.is_root());
self.ctx_trace.resize(trace_len, ZERO);
let mut trace = vec![self.clk_trace, self.ctx_trace];
debug_assert_eq!(self.fn_hash, EMPTY_WORD);
for mut column in self.fn_hash_trace.into_iter() {
column.resize(trace_len, ZERO);
trace.push(column);
}
trace.try_into().expect("failed to convert vector to array")
}
pub fn ensure_trace_capacity(&mut self) {
let current_capacity = self.clk_trace.len();
if self.clk + 1 >= RowIndex::from(current_capacity) {
let new_length = current_capacity * 2;
self.clk_trace.resize(new_length, ZERO);
self.ctx_trace.resize(new_length, ZERO);
for column in self.fn_hash_trace.iter_mut() {
column.resize(new_length, ZERO);
}
}
}
pub fn get_next_ctx_id(&self) -> ContextId {
(self.clk + 1).into()
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Ord, PartialEq, PartialOrd)]
pub struct ContextId(u32);
impl ContextId {
pub fn root() -> Self {
Self(0)
}
pub fn is_root(&self) -> bool {
self.0 == 0
}
}
impl From<RowIndex> for ContextId {
fn from(value: RowIndex) -> Self {
Self(value.as_u32())
}
}
impl From<u32> for ContextId {
fn from(value: u32) -> Self {
Self(value)
}
}
impl From<ContextId> for u32 {
fn from(context_id: ContextId) -> Self {
context_id.0
}
}
impl From<ContextId> for u64 {
fn from(context_id: ContextId) -> Self {
context_id.0.into()
}
}
impl From<ContextId> for Felt {
fn from(context_id: ContextId) -> Self {
context_id.0.into()
}
}
impl Display for ContextId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.0)
}
}