use miden_air::trace::RowIndex;
use miden_core::field::{PrimeCharacteristicRing, QuadFelt};
use crate::{
ContextId, Felt,
errors::{AceError, AceEvalError},
processor::{MemoryInterface, Processor, StackInterface, SystemInterface},
trace::chiplets::{CircuitEvaluation, MAX_NUM_ACE_WIRES, PTR_OFFSET_ELEM, PTR_OFFSET_WORD},
tracer::Tracer,
};
#[inline(always)]
pub(super) fn op_eval_circuit<P, T>(processor: &mut P, tracer: &mut T) -> Result<(), AceEvalError>
where
P: Processor,
T: Tracer<Processor = P>,
{
let num_eval = processor.stack().get(2);
let num_read = processor.stack().get(1);
let ptr = processor.stack().get(0);
let ctx = processor.system().ctx();
let clk = processor.system().clock();
let circuit_evaluation =
eval_circuit_impl(ctx, ptr, clk, num_read, num_eval, processor.memory_mut())?;
tracer.record_circuit_evaluation(circuit_evaluation);
Ok(())
}
pub(crate) fn eval_circuit_impl(
ctx: ContextId,
ptr: Felt,
clk: RowIndex,
num_vars: Felt,
num_eval: Felt,
mem: &mut impl MemoryInterface,
) -> Result<CircuitEvaluation, AceEvalError> {
let num_vars = num_vars.as_canonical_u64();
let num_eval = num_eval.as_canonical_u64();
let num_wires = num_vars.saturating_add(num_eval);
if num_wires > MAX_NUM_ACE_WIRES as u64 {
const {
assert!(MAX_NUM_ACE_WIRES == (1_u32 << 30) - 1);
}
return Err(
AceError(format!("num of wires must be less than 2^30 but was {num_wires}")).into()
);
}
if !num_vars.is_multiple_of(2) || num_vars == 0 {
return Err(AceError(format!(
"num of variables should be word aligned and non-zero but was {num_vars}"
))
.into());
}
if !num_eval.is_multiple_of(4) || num_eval == 0 {
return Err(AceError(format!(
"num of evaluation gates should be word aligned and non-zero but was {num_eval}"
))
.into());
}
let num_read_rows = (num_vars / 2) as u32;
let num_eval_rows = num_eval as u32;
let mut evaluation_context = CircuitEvaluation::new(ctx, clk, num_read_rows, num_eval_rows);
let mut ptr = ptr;
for _ in 0..num_read_rows {
let word = mem.read_word(ctx, ptr, clk)?;
evaluation_context.do_read(ptr, word);
ptr += PTR_OFFSET_WORD;
}
for _ in 0..num_eval_rows {
let instruction = mem.read_element(ctx, ptr)?;
evaluation_context.do_eval(ptr, instruction)?;
ptr += PTR_OFFSET_ELEM;
}
if evaluation_context.output_value().is_none_or(|eval| eval != QuadFelt::ZERO) {
return Err(AceError("circuit does not evaluate to zero".into()).into());
}
Ok(evaluation_context)
}