use alloc::vec::Vec;
use paste::paste;
use crate::{
ExecutionError, Felt, ZERO,
operation::OperationError,
processor::{Processor, StackInterface, SystemInterface},
tracer::{OperationHelperRegisters, Tracer},
};
#[cfg(test)]
mod tests;
const U32_MAX: u64 = u32::MAX as u64;
macro_rules! require_u32_operands {
($processor:expr, [$($idx:expr),*]) => {{
let mut invalid_values = Vec::new();
paste!{
$(
let [<operand_ $idx>] = $processor.stack().get($idx);
if [<operand_ $idx>].as_canonical_u64() > U32_MAX {
invalid_values.push([<operand_ $idx>]);
}
)*
if !invalid_values.is_empty() {
return Err(OperationError::NotU32Values { values: invalid_values });
}
($([<operand_ $idx>]),*)
}
}};
}
#[inline(always)]
pub(super) fn op_u32split<P, T>(
processor: &mut P,
tracer: &mut T,
) -> Result<OperationHelperRegisters, ExecutionError>
where
P: Processor,
T: Tracer<Processor = P>,
{
let (top_hi, top_lo) = {
let top = processor.stack().get(0);
split_element(top)
};
tracer.record_u32_range_checks(processor.system().clock(), top_lo, top_hi);
processor.stack_mut().increment_size()?;
processor.stack_mut().set(0, top_lo);
processor.stack_mut().set(1, top_hi);
Ok(OperationHelperRegisters::U32Split { lo: top_lo, hi: top_hi })
}
#[inline(always)]
pub(super) fn op_u32add<P: Processor, T: Tracer>(
processor: &mut P,
tracer: &mut T,
) -> Result<OperationHelperRegisters, OperationError> {
let (carry, sum) = {
let (a, b) = require_u32_operands!(processor, [0, 1]);
let result = Felt::new(a.as_canonical_u64() + b.as_canonical_u64());
split_element(result)
};
tracer.record_u32_range_checks(processor.system().clock(), sum, carry);
processor.stack_mut().set(0, sum);
processor.stack_mut().set(1, carry);
Ok(OperationHelperRegisters::U32Add { sum, carry })
}
#[inline(always)]
pub(super) fn op_u32add3<P, T>(
processor: &mut P,
tracer: &mut T,
) -> Result<OperationHelperRegisters, OperationError>
where
P: Processor,
T: Tracer<Processor = P>,
{
let (carry, sum) = {
let (a, b, c) = require_u32_operands!(processor, [0, 1, 2]);
let result = Felt::new(a.as_canonical_u64() + b.as_canonical_u64() + c.as_canonical_u64());
split_element(result)
};
tracer.record_u32_range_checks(processor.system().clock(), sum, carry);
processor.stack_mut().decrement_size()?;
processor.stack_mut().set(0, sum);
processor.stack_mut().set(1, carry);
Ok(OperationHelperRegisters::U32Add3 { sum, carry })
}
#[inline(always)]
pub(super) fn op_u32sub<P: Processor, T: Tracer>(
processor: &mut P,
tracer: &mut T,
) -> Result<OperationHelperRegisters, OperationError> {
let (b, a) = require_u32_operands!(processor, [0, 1]);
let result = a.as_canonical_u64().wrapping_sub(b.as_canonical_u64());
let borrow = Felt::new(result >> 63);
let diff = Felt::new(result & u32::MAX as u64);
tracer.record_u32_range_checks(processor.system().clock(), diff, ZERO);
processor.stack_mut().set(0, borrow);
processor.stack_mut().set(1, diff);
Ok(OperationHelperRegisters::U32Sub { second_new: diff })
}
#[inline(always)]
pub(super) fn op_u32mul<P: Processor, T: Tracer>(
processor: &mut P,
tracer: &mut T,
) -> Result<OperationHelperRegisters, OperationError> {
let (a, b) = require_u32_operands!(processor, [0, 1]);
let result = Felt::new(a.as_canonical_u64() * b.as_canonical_u64());
let (hi, lo) = split_element(result);
tracer.record_u32_range_checks(processor.system().clock(), lo, hi);
processor.stack_mut().set(0, lo);
processor.stack_mut().set(1, hi);
Ok(OperationHelperRegisters::U32Mul { lo, hi })
}
#[inline(always)]
pub(super) fn op_u32madd<P, T>(
processor: &mut P,
tracer: &mut T,
) -> Result<OperationHelperRegisters, OperationError>
where
P: Processor,
T: Tracer<Processor = P>,
{
let (a, b, c) = require_u32_operands!(processor, [0, 1, 2]);
let result = Felt::new(a.as_canonical_u64() * b.as_canonical_u64() + c.as_canonical_u64());
let (hi, lo) = split_element(result);
tracer.record_u32_range_checks(processor.system().clock(), lo, hi);
processor.stack_mut().decrement_size()?;
processor.stack_mut().set(0, lo);
processor.stack_mut().set(1, hi);
Ok(OperationHelperRegisters::U32Madd { lo, hi })
}
#[inline(always)]
pub(super) fn op_u32div<P: Processor, T: Tracer>(
processor: &mut P,
tracer: &mut T,
) -> Result<OperationHelperRegisters, OperationError> {
let (denominator, numerator) = {
let (b, a) = require_u32_operands!(processor, [0, 1]);
(b.as_canonical_u64(), a.as_canonical_u64())
};
if denominator == 0 {
return Err(OperationError::DivideByZero);
}
let quotient = numerator / denominator;
let remainder = numerator - quotient * denominator;
processor.stack_mut().set(0, Felt::new(remainder));
processor.stack_mut().set(1, Felt::new(quotient));
let lo = Felt::new(numerator - quotient);
let hi = Felt::new(denominator - remainder - 1);
tracer.record_u32_range_checks(processor.system().clock(), lo, hi);
Ok(OperationHelperRegisters::U32Div { lo, hi })
}
#[inline(always)]
pub(super) fn op_u32and<P, T>(
processor: &mut P,
tracer: &mut T,
) -> Result<OperationHelperRegisters, OperationError>
where
P: Processor,
T: Tracer<Processor = P>,
{
let (a, b) = require_u32_operands!(processor, [0, 1]);
tracer.record_u32and(a, b);
let result = a.as_canonical_u64() & b.as_canonical_u64();
processor.stack_mut().decrement_size()?;
processor.stack_mut().set(0, Felt::new(result));
Ok(OperationHelperRegisters::Empty)
}
#[inline(always)]
pub(super) fn op_u32xor<P, T>(
processor: &mut P,
tracer: &mut T,
) -> Result<OperationHelperRegisters, OperationError>
where
P: Processor,
T: Tracer<Processor = P>,
{
let (a, b) = require_u32_operands!(processor, [0, 1]);
tracer.record_u32xor(a, b);
let result = a.as_canonical_u64() ^ b.as_canonical_u64();
processor.stack_mut().decrement_size()?;
processor.stack_mut().set(0, Felt::new(result));
Ok(OperationHelperRegisters::Empty)
}
#[inline(always)]
pub(super) fn op_u32assert2<P: Processor, T: Tracer>(
processor: &mut P,
_err_code: Felt,
tracer: &mut T,
) -> Result<OperationHelperRegisters, OperationError> {
let (first, second) = require_u32_operands!(processor, [0, 1]);
tracer.record_u32_range_checks(processor.system().clock(), first, second);
Ok(OperationHelperRegisters::U32Assert2 { first, second })
}
#[inline(always)]
fn split_element(value: Felt) -> (Felt, Felt) {
let value = value.as_canonical_u64();
let lo = (value as u32) as u64;
let hi = value >> 32;
(Felt::new(hi), Felt::new(lo))
}