use vm_core::{Felt, FieldElement, ONE, ZERO};
use super::{ExecutionError, FastProcessor};
use crate::{ErrorContext, operations::utils::assert_binary};
const TWO: Felt = Felt::new(2);
impl FastProcessor {
pub fn op_add(&mut self) -> Result<(), ExecutionError> {
self.pop2_applyfn_push(|a, b| Ok(a + b))
}
pub fn op_neg(&mut self) -> Result<(), ExecutionError> {
let element = self.stack_get(0);
self.stack_write(0, -element);
Ok(())
}
pub fn op_mul(&mut self) -> Result<(), ExecutionError> {
self.pop2_applyfn_push(|a, b| Ok(a * b))
}
pub fn op_inv(&mut self, op_idx: usize) -> Result<(), ExecutionError> {
let top = self.stack_get_mut(0);
if (*top) == ZERO {
return Err(ExecutionError::divide_by_zero(
self.clk + op_idx,
&ErrorContext::default(),
));
}
*top = top.inv();
Ok(())
}
pub fn op_incr(&mut self) -> Result<(), ExecutionError> {
*self.stack_get_mut(0) += ONE;
Ok(())
}
pub fn op_and(&mut self) -> Result<(), ExecutionError> {
self.pop2_applyfn_push(|a, b| {
assert_binary(b)?;
assert_binary(a)?;
if a == ONE && b == ONE { Ok(ONE) } else { Ok(ZERO) }
})
}
pub fn op_or(&mut self) -> Result<(), ExecutionError> {
self.pop2_applyfn_push(|a, b| {
assert_binary(b)?;
assert_binary(a)?;
if a == ONE || b == ONE { Ok(ONE) } else { Ok(ZERO) }
})
}
pub fn op_not(&mut self) -> Result<(), ExecutionError> {
let top = self.stack_get_mut(0);
if *top == ZERO {
*top = ONE;
} else if *top == ONE {
*top = ZERO;
} else {
return Err(ExecutionError::not_binary_value_op(*top, &ErrorContext::default()));
}
Ok(())
}
pub fn op_eq(&mut self) -> Result<(), ExecutionError> {
self.pop2_applyfn_push(|a, b| if a == b { Ok(ONE) } else { Ok(ZERO) })
}
pub fn op_eqz(&mut self) -> Result<(), ExecutionError> {
let top = self.stack_get_mut(0);
if (*top) == ZERO {
*top = ONE;
} else {
*top = ZERO;
}
Ok(())
}
pub fn op_expacc(&mut self) {
let old_base = self.stack_get(1);
let old_acc = self.stack_get(2);
let old_exp_int = self.stack_get(3).as_int();
let new_exp = Felt::new(old_exp_int >> 1);
let exp_lsb = old_exp_int & 1;
let acc_update_val = if exp_lsb == 1 { old_base } else { ONE };
let new_acc = old_acc * acc_update_val;
let new_base = old_base * old_base;
self.stack_write(0, Felt::new(exp_lsb));
self.stack_write(1, new_base);
self.stack_write(2, new_acc);
self.stack_write(3, new_exp);
}
pub fn op_ext2mul(&mut self) {
let [a0, a1, b0, b1] = self.stack_get_word(0);
let b0_times_a0 = b0 * a0;
self.stack_write(2, (b0 + b1) * (a1 + a0) - b0_times_a0);
self.stack_write(3, b0_times_a0 - TWO * b1 * a1);
}
#[inline(always)]
fn pop2_applyfn_push(
&mut self,
f: impl FnOnce(Felt, Felt) -> Result<Felt, ExecutionError>,
) -> Result<(), ExecutionError> {
let b = self.stack_get(0);
let a = self.stack_get(1);
self.decrement_stack_size();
self.stack_write(0, f(a, b)?);
Ok(())
}
}