use alloc::{collections::BTreeMap, vec::Vec};
use miden_core::{Felt, field::QuadFelt, utils::RowMajorMatrix};
use super::{
CELL_GROUP, CELL_R, CELL_SBOUND, COL_A_PTR, COL_ACT, COL_B_PTR, COL_BETA_PTR, COL_BOUND_PTR,
COL_CANCEL, COL_DBL, COL_GEN, COL_LAMBDA_PTR, COL_MINTS, COL_PAI_P, COL_PAI_Q, COL_PX, COL_PY,
COL_QX, COL_QY, COL_RP_HI, COL_RP_LO, COL_RQ_HI, COL_RQ_LO, EcGroupAddAir, NUM_CELLS,
NUM_MAIN_COLS, PERIOD, ROW_RES, ROW_SLOPE, ROW_TAIL, ROW_TERM, TERM_CELL_MULT, TERM_CELL_P,
TERM_CELL_Q,
};
use crate::{
ec::trace::{EcGroupPtr, EcPointPtr, EcStoreRequires},
logup::build_logup_aux_trace,
primitives::byte_pair_lut::BytePairLutRequires,
relations::ProvideMult,
uint::trace::UintPtr,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) enum EcAddCase {
PaiP,
PaiQ,
PaiBoth,
Cancel,
Double,
Generic,
}
impl EcAddCase {
pub(crate) fn flags(self) -> [bool; 5] {
match self {
Self::PaiP => [true, false, false, false, false],
Self::PaiQ => [false, true, false, false, false],
Self::PaiBoth => [true, true, false, false, false],
Self::Cancel => [false, false, true, false, false],
Self::Double => [false, false, false, true, false],
Self::Generic => [false, false, false, false, true],
}
}
}
#[derive(Debug, Clone, Copy)]
pub(crate) struct EcAddOp {
pub case: EcAddCase,
pub group: EcGroupPtr,
pub bound: UintPtr,
pub a: UintPtr,
pub b: UintPtr,
pub beta: UintPtr,
pub lambda: UintPtr,
pub p: EcPointPtr,
pub q: EcPointPtr,
pub r: EcPointPtr,
pub p_coords: Option<(UintPtr, UintPtr)>,
pub q_coords: Option<(UintPtr, UintPtr)>,
pub transients: Option<[UintPtr; 6]>,
pub mints: bool,
}
#[derive(Debug, Default)]
pub struct EcAddRequires {
pub(crate) ops: Vec<(EcAddOp, ProvideMult)>,
dedup: BTreeMap<(EcGroupPtr, EcPointPtr, EcPointPtr), usize>,
}
impl EcAddRequires {
pub fn new() -> Self {
Self::default()
}
pub(crate) fn consume(
&mut self,
group: EcGroupPtr,
p: EcPointPtr,
q: EcPointPtr,
mult: ProvideMult,
) -> Option<EcPointPtr> {
let &i = self.dedup.get(&(group, p, q))?;
self.ops[i].1 += mult;
Some(self.ops[i].0.r)
}
pub(crate) fn record(&mut self, op: EcAddOp, mult: ProvideMult) {
self.dedup.insert((op.group, op.p, op.q), self.ops.len());
self.ops.push((op, mult));
}
}
fn op_block(op: &EcAddOp, mult: ProvideMult, ec: &EcStoreRequires) -> Vec<Felt> {
let mut block = [[Felt::ZERO; NUM_MAIN_COLS]; PERIOD];
let mut set = |row: usize, col: usize, v: u32| block[row][col] = Felt::from(v);
let transients = op.transients.map_or([0u32; 6], |t| t.map(UintPtr::addr));
for (cell, ptr) in transients[..NUM_CELLS].iter().enumerate() {
set(ROW_SLOPE, cell, *ptr);
}
for (cell, ptr) in transients[NUM_CELLS..2 * NUM_CELLS].iter().enumerate() {
set(ROW_TAIL, cell, *ptr);
}
set(ROW_RES, CELL_R, op.r.addr());
set(ROW_RES, CELL_SBOUND, ec.group_sbound(op.group).addr());
set(ROW_RES, CELL_GROUP, op.group.addr());
set(ROW_TERM, TERM_CELL_P, op.p.addr());
set(ROW_TERM, TERM_CELL_Q, op.q.addr());
let [pai_p, pai_q, cancel, dbl, generic] = op.case.flags();
let coord = |c: Option<(UintPtr, UintPtr)>, y: bool| -> u32 {
c.map_or(0, |(cx, cy)| if y { cy.addr() } else { cx.addr() })
};
let (rp_lo, rp_hi, rq_lo, rq_hi) = if op.mints {
let rp = op.r.addr() - op.p.addr() - 1;
let rq = op.r.addr() - op.q.addr() - 1;
(rp & 0xffff, rp >> 16, rq & 0xffff, rq >> 16)
} else {
(0, 0, 0, 0)
};
for row in 0..PERIOD {
set(row, COL_PX, coord(op.p_coords, false));
set(row, COL_PY, coord(op.p_coords, true));
set(row, COL_QX, coord(op.q_coords, false));
set(row, COL_QY, coord(op.q_coords, true));
set(row, COL_A_PTR, op.a.addr());
set(row, COL_B_PTR, op.b.addr());
set(row, COL_BOUND_PTR, op.bound.addr());
set(row, COL_BETA_PTR, op.beta.addr());
set(row, COL_LAMBDA_PTR, op.lambda.addr());
set(row, COL_PAI_P, u32::from(pai_p));
set(row, COL_PAI_Q, u32::from(pai_q));
set(row, COL_CANCEL, u32::from(cancel));
set(row, COL_DBL, u32::from(dbl));
set(row, COL_GEN, u32::from(generic));
set(row, COL_ACT, 1);
set(row, COL_MINTS, u32::from(op.mints));
set(row, COL_RP_LO, rp_lo);
set(row, COL_RP_HI, rp_hi);
set(row, COL_RQ_LO, rq_lo);
set(row, COL_RQ_HI, rq_hi);
}
set(ROW_TERM, TERM_CELL_MULT, mult);
block.into_iter().flatten().collect()
}
pub fn generate_trace(
requires: EcAddRequires,
ec: &mut EcStoreRequires,
bpl: &mut BytePairLutRequires,
) -> RowMajorMatrix<Felt> {
let height = (requires.ops.len().max(1) * PERIOD).next_power_of_two();
let mut vals = Vec::with_capacity(height * NUM_MAIN_COLS);
for (op, mult) in &requires.ops {
ec.require_ecpoint(op.p);
ec.require_ecpoint(op.q);
let [_, _, cancel, dbl, generic] = op.case.flags();
if cancel || dbl || generic {
ec.require_ecgroup(op.group);
ec.require_ecpoint(op.r);
}
if op.mints {
let rp = op.r.addr() - op.p.addr() - 1;
let rq = op.r.addr() - op.q.addr() - 1;
bpl.require_range16((rp & 0xffff) as u16);
bpl.require_range16((rp >> 16) as u16);
bpl.require_range16((rq & 0xffff) as u16);
bpl.require_range16((rq >> 16) as u16);
}
vals.extend(op_block(op, *mult, ec));
}
vals.resize(height * NUM_MAIN_COLS, Felt::ZERO);
RowMajorMatrix::new(vals, NUM_MAIN_COLS)
}
pub(crate) fn build_aux(
main: &RowMajorMatrix<Felt>,
challenges: &[QuadFelt],
) -> (RowMajorMatrix<QuadFelt>, Vec<QuadFelt>) {
build_logup_aux_trace(&EcGroupAddAir, main, challenges)
}