use crate::{
Felt, ONE, ZERO,
field::Field,
operation::OperationError,
processor::{Processor, StackInterface},
tracer::OperationHelperRegisters,
};
#[cfg(test)]
mod tests;
#[inline(always)]
pub(super) fn op_add<P>(processor: &mut P) -> Result<OperationHelperRegisters, OperationError>
where
P: Processor,
{
pop2_applyfn_push(processor, |a, b| a + b)?;
Ok(OperationHelperRegisters::Empty)
}
#[inline(always)]
pub(super) fn op_neg<P: Processor>(processor: &mut P) -> OperationHelperRegisters {
let element = processor.stack().get(0);
processor.stack_mut().set(0, -element);
OperationHelperRegisters::Empty
}
#[inline(always)]
pub(super) fn op_mul<P>(processor: &mut P) -> Result<OperationHelperRegisters, OperationError>
where
P: Processor,
{
pop2_applyfn_push(processor, |a, b| a * b)?;
Ok(OperationHelperRegisters::Empty)
}
#[inline(always)]
pub(super) fn op_inv<P: Processor>(
processor: &mut P,
) -> Result<OperationHelperRegisters, OperationError> {
let top = processor.stack_mut().get_mut(0);
if (*top) == ZERO {
return Err(OperationError::DivideByZero);
}
*top = top.inverse();
Ok(OperationHelperRegisters::Empty)
}
#[inline(always)]
pub(super) fn op_incr<P: Processor>(processor: &mut P) -> OperationHelperRegisters {
*processor.stack_mut().get_mut(0) += ONE;
OperationHelperRegisters::Empty
}
#[inline(always)]
pub(super) fn op_and<P>(processor: &mut P) -> Result<OperationHelperRegisters, OperationError>
where
P: Processor,
{
pop2_applyfn_push_op(processor, |a, b| {
assert_binary(b)?;
assert_binary(a)?;
if a == ONE && b == ONE { Ok(ONE) } else { Ok(ZERO) }
})?;
Ok(OperationHelperRegisters::Empty)
}
#[inline(always)]
pub(super) fn op_or<P>(processor: &mut P) -> Result<OperationHelperRegisters, OperationError>
where
P: Processor,
{
pop2_applyfn_push_op(processor, |a, b| {
assert_binary(b)?;
assert_binary(a)?;
if a == ONE || b == ONE { Ok(ONE) } else { Ok(ZERO) }
})?;
Ok(OperationHelperRegisters::Empty)
}
#[inline(always)]
pub(super) fn op_not<P: Processor>(
processor: &mut P,
) -> Result<OperationHelperRegisters, OperationError> {
let top = processor.stack_mut().get_mut(0);
if *top == ZERO {
*top = ONE;
} else if *top == ONE {
*top = ZERO;
} else {
return Err(OperationError::NotBinaryValue { value: *top });
}
Ok(OperationHelperRegisters::Empty)
}
#[inline(always)]
pub(super) fn op_eq<P>(processor: &mut P) -> Result<OperationHelperRegisters, OperationError>
where
P: Processor,
{
let b = processor.stack().get(0);
let a = processor.stack().get(1);
processor.stack_mut().decrement_size()?;
let result = if a == b { ONE } else { ZERO };
processor.stack_mut().set(0, result);
Ok(OperationHelperRegisters::Eq { stack_second: a, stack_first: b })
}
#[inline(always)]
pub(super) fn op_eqz<P: Processor>(processor: &mut P) -> OperationHelperRegisters {
let top = processor.stack_mut().get_mut(0);
let old_top = *top;
if old_top == ZERO {
*top = ONE;
} else {
*top = ZERO;
};
OperationHelperRegisters::Eqz { top: old_top }
}
#[inline(always)]
pub(super) fn op_expacc<P: Processor>(processor: &mut P) -> OperationHelperRegisters {
let old_base = processor.stack().get(1);
let old_acc = processor.stack().get(2);
let old_exp_int = processor.stack().get(3).as_canonical_u64();
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;
processor.stack_mut().set(0, Felt::new(exp_lsb));
processor.stack_mut().set(1, new_base);
processor.stack_mut().set(2, new_acc);
processor.stack_mut().set(3, new_exp);
OperationHelperRegisters::Expacc { acc_update_val }
}
#[inline(always)]
pub(super) fn op_ext2mul<P: Processor>(processor: &mut P) -> OperationHelperRegisters {
const SEVEN: Felt = Felt::new(7);
let [b0, b1, a0, a1]: [Felt; 4] = processor.stack().get_word(0).into();
let b0_times_a0 = b0 * a0;
let b1_times_a1 = b1 * a1;
let c1 = (b0 + b1) * (a1 + a0) - b0_times_a0 - b1_times_a1;
let c0 = b0_times_a0 + SEVEN * b1_times_a1;
processor.stack_mut().set(2, c0); processor.stack_mut().set(3, c1); OperationHelperRegisters::Empty
}
#[inline(always)]
fn pop2_applyfn_push<P>(
processor: &mut P,
f: impl FnOnce(Felt, Felt) -> Felt,
) -> Result<(), OperationError>
where
P: Processor,
{
let b = processor.stack().get(0);
let a = processor.stack().get(1);
processor.stack_mut().decrement_size()?;
processor.stack_mut().set(0, f(a, b));
Ok(())
}
#[inline(always)]
fn pop2_applyfn_push_op<P>(
processor: &mut P,
f: impl FnOnce(Felt, Felt) -> Result<Felt, OperationError>,
) -> Result<(), OperationError>
where
P: Processor,
{
let b = processor.stack().get(0);
let a = processor.stack().get(1);
processor.stack_mut().decrement_size()?;
processor.stack_mut().set(0, f(a, b)?);
Ok(())
}
#[inline(always)]
fn assert_binary(value: Felt) -> Result<(), OperationError> {
if value != ZERO && value != ONE {
Err(OperationError::NotBinaryValue { value })
} else {
Ok(())
}
}