use super::{
ByteWriter, Felt, OutputError, Serializable, StackTopState, StarkField, ToElements, Vec,
STACK_TOP_SIZE,
};
#[derive(Debug, Clone, Default, PartialEq, Eq)]
pub struct StackOutputs {
stack: Vec<u64>,
overflow_addrs: Vec<u64>,
}
impl StackOutputs {
pub fn new(mut stack: Vec<u64>, overflow_addrs: Vec<u64>) -> Result<Self, OutputError> {
if let Some(element) = find_invalid_elements(&stack) {
return Err(OutputError::InvalidStackElement(element));
}
if let Some(element) = find_invalid_elements(&overflow_addrs) {
return Err(OutputError::InvalidOverflowAddress(element));
}
if stack.len() < STACK_TOP_SIZE {
stack.resize(STACK_TOP_SIZE, 0);
}
let expected_overflow_addrs_len = if stack.len() > STACK_TOP_SIZE {
stack.len() + 1 - STACK_TOP_SIZE
} else {
0
};
if overflow_addrs.len() != expected_overflow_addrs_len {
return Err(OutputError::InvalidOverflowAddressLength(
overflow_addrs.len(),
expected_overflow_addrs_len,
));
}
Ok(Self {
stack,
overflow_addrs,
})
}
pub fn from_elements(stack: Vec<Felt>, overflow_addrs: Vec<Felt>) -> Result<Self, OutputError> {
let stack = stack.iter().map(|&v| v.as_int()).collect::<Vec<_>>();
let overflow_addrs = overflow_addrs.iter().map(|&v| v.as_int()).collect::<Vec<_>>();
Self::new(stack, overflow_addrs)
}
pub fn stack(&self) -> &[u64] {
&self.stack
}
pub fn stack_truncated(&self, num_outputs: usize) -> &[u64] {
let len = self.stack.len().min(num_outputs);
&self.stack[..len]
}
pub fn stack_top(&self) -> StackTopState {
self.stack
.iter()
.take(STACK_TOP_SIZE)
.map(|v| Felt::new(*v))
.collect::<Vec<_>>()
.try_into()
.expect("failed to convert vector to array")
}
pub fn overflow_addrs(&self) -> &[u64] {
&self.overflow_addrs
}
pub fn has_overflow(&self) -> bool {
!self.overflow_addrs.is_empty()
}
pub fn overflow_prev(&self) -> Felt {
Felt::new(self.overflow_addrs[0])
}
pub fn stack_overflow(&self) -> Vec<(Felt, Felt)> {
let mut overflow = Vec::with_capacity(self.overflow_addrs.len() - 1);
for (addr, val) in self
.overflow_addrs
.iter()
.skip(1)
.zip(self.stack.iter().skip(STACK_TOP_SIZE).rev())
{
overflow.push((Felt::new(*addr), Felt::new(*val)));
}
overflow
}
pub fn stack_mut(&mut self) -> &mut [u64] {
&mut self.stack
}
}
fn find_invalid_elements(outputs: &[u64]) -> Option<u64> {
for val in outputs {
if *val >= Felt::MODULUS {
return Some(*val);
}
}
None
}
impl Serializable for StackOutputs {
fn write_into<W: ByteWriter>(&self, target: &mut W) {
debug_assert!(self.stack.len() <= u32::MAX as usize);
target.write_u32(self.stack.len() as u32);
self.stack.iter().copied().for_each(|v| target.write_u64(v));
debug_assert!(self.overflow_addrs.len() <= u32::MAX as usize);
target.write_u32(self.overflow_addrs.len() as u32);
self.overflow_addrs.iter().copied().for_each(|v| target.write_u64(v));
}
}
impl ToElements<Felt> for StackOutputs {
fn to_elements(&self) -> Vec<Felt> {
self.stack
.iter()
.chain(self.overflow_addrs.iter())
.cloned()
.map(Felt::new)
.collect()
}
}