pub mod trace;
use alloc::{vec, vec::Vec};
use core::array;
use miden_core::{
Felt,
field::{Algebra, PrimeCharacteristicRing, QuadFelt},
utils::RowMajorMatrix,
};
use miden_lifted_air::{BaseAir, LiftedAir, LiftedAirBuilder};
use crate::{
logup::{
Challenges, CyclicConstraintLookupBuilder, Deg, LookupAir, LookupBatch, LookupBuilder,
LookupColumn, LookupGroup, LookupMessage, NUM_PUBLIC_VALUES, NUM_RANDOMNESS,
NUM_SIGMA_VALUES,
},
relations::{BusId, MAX_MESSAGE_WIDTH, NUM_BUS_IDS},
uint::UintValMsg,
utils::{current_main, next_main},
};
#[derive(Debug, Clone)]
pub struct UintAddMsg<E> {
pub bound_ptr: E,
pub a_ptr: E,
pub b_ptr: E,
pub c_ptr: E,
pub nz: E,
}
impl<E, EF> LookupMessage<E, EF> for UintAddMsg<E>
where
E: Algebra<E>,
EF: Algebra<E>,
{
fn encode(&self, challenges: &Challenges<EF>) -> EF {
challenges.encode(
BusId::UintAdd as usize,
[
self.bound_ptr.clone(),
self.a_ptr.clone(),
self.b_ptr.clone(),
self.c_ptr.clone(),
self.nz.clone(),
],
)
}
}
pub const NUM_LIMBS: usize = 8;
pub const CELL_HI: usize = NUM_LIMBS;
pub const NUM_CELLS: usize = 24;
pub const CELL_FLAG: usize = 20;
pub const CELL_IS_B_ZERO: usize = CELL_FLAG;
pub const CELL_K: usize = CELL_FLAG;
pub const CELL_D_W: usize = 21;
pub const CELL_C_ON: usize = 21;
pub const CELL_D_WS: usize = 22;
pub const TERM_CELL_MULT: usize = 22;
pub const CELL_B_ON: usize = 23;
pub const CELL_IS_C_ZERO: usize = 23;
pub const COL_A_PTR: usize = NUM_CELLS;
pub const COL_B_PTR: usize = NUM_CELLS + 1;
pub const COL_C_PTR: usize = NUM_CELLS + 2;
pub const COL_BOUND_PTR: usize = NUM_CELLS + 3;
pub const COL_ACT: usize = NUM_CELLS + 4;
pub const COL_NZ: usize = NUM_CELLS + 5;
pub const NUM_MAIN_COLS: usize = NUM_CELLS + 6;
pub const PERIOD: usize = 2;
pub const ROW_AB: usize = 0;
pub const ROW_CP: usize = 1;
pub const NUM_GAMMA: usize = 7;
pub const FIRST_GAMMA_COL: usize = 16;
pub const NUM_GAMMA_COLS: usize = 4;
pub const GAMMA_SLOTS: [(usize, usize); NUM_GAMMA] = [
(ROW_AB, 16),
(ROW_AB, 17),
(ROW_AB, 18),
(ROW_AB, 19),
(ROW_CP, 16),
(ROW_CP, 17),
(ROW_CP, 18),
];
const NUM_LOGUP_COLS: usize = 3;
const AUX_WIDTH: usize = 3;
const COLUMN_SHAPE: [usize; NUM_LOGUP_COLS] = [1, 2, 2];
#[derive(Debug, Default, Clone, Copy)]
pub struct UintAddAir;
impl BaseAir<Felt> for UintAddAir {
fn width(&self) -> usize {
NUM_MAIN_COLS
}
fn num_public_values(&self) -> usize {
NUM_PUBLIC_VALUES
}
fn periodic_columns(&self) -> Vec<Vec<Felt>> {
vec![vec![Felt::ONE, Felt::ZERO]]
}
}
impl LiftedAir<Felt, QuadFelt> for UintAddAir {
fn num_randomness(&self) -> usize {
NUM_RANDOMNESS
}
fn aux_width(&self) -> usize {
AUX_WIDTH
}
fn num_aux_values(&self) -> usize {
NUM_SIGMA_VALUES
}
fn build_aux_trace(
&self,
main: &RowMajorMatrix<Felt>,
_air_inputs: &[Felt],
_aux_inputs: &[Felt],
challenges: &[QuadFelt],
) -> (RowMajorMatrix<QuadFelt>, Vec<QuadFelt>) {
trace::build_aux(main, challenges)
}
fn eval<AB: LiftedAirBuilder<F = Felt>>(&self, builder: &mut AB) {
let local: [AB::Var; NUM_MAIN_COLS] = current_main(builder.main(), 0);
let next: [AB::Var; NUM_MAIN_COLS] = next_main(builder.main(), 0);
let ab_sel: AB::Expr = builder.periodic_values()[0].into();
let cp_sel: AB::Expr = AB::Expr::ONE - ab_sel.clone();
let beta: AB::ExprEF = builder.permutation_randomness()[1].into();
let mut bp: Vec<AB::ExprEF> = Vec::with_capacity(8);
bp.push(AB::ExprEF::ONE);
for i in 1..8 {
bp.push(bp[i - 1].clone() * beta.clone());
}
let t32: AB::Expr = AB::Expr::from(Felt::new(1u64 << 32).expect("2^32 < Goldilocks p"));
let a_beta: AB::ExprEF = (0..NUM_LIMBS)
.fold(AB::ExprEF::ZERO, |s, j| s + bp[j].clone() * AB::Expr::from(local[j]));
let b_beta: AB::ExprEF = (0..NUM_LIMBS)
.fold(AB::ExprEF::ZERO, |s, j| s + bp[j].clone() * AB::Expr::from(local[CELL_HI + j]));
let c_beta: AB::ExprEF = (0..NUM_LIMBS)
.fold(AB::ExprEF::ZERO, |s, j| s + bp[j].clone() * AB::Expr::from(next[j]));
let p_beta: AB::ExprEF = (0..NUM_LIMBS)
.fold(AB::ExprEF::ZERO, |s, j| s + bp[j].clone() * AB::Expr::from(next[CELL_HI + j]));
let is_b_zero: AB::Expr = local[CELL_IS_B_ZERO].into();
let is_c_zero_next: AB::Expr = next[CELL_IS_C_ZERO].into();
let k_next: AB::Expr = next[CELL_K].into();
let mut carry: AB::ExprEF = AB::ExprEF::ZERO;
for (j, &(row, cell)) in GAMMA_SLOTS.iter().enumerate() {
let w: AB::ExprEF = bp[j + 1].clone() - bp[j].clone() * t32.clone();
let g: AB::Expr = if row == ROW_AB {
local[cell].into()
} else {
next[cell].into()
};
carry += w * g;
}
let identity: AB::ExprEF = a_beta + b_beta * (AB::Expr::ONE - is_b_zero.clone())
- c_beta * (AB::Expr::ONE - is_c_zero_next)
- (p_beta + bp[0].clone()) * k_next
+ carry;
builder.assert_zero_ext(identity * ab_sel.clone());
for &cell in &local[FIRST_GAMMA_COL..FIRST_GAMMA_COL + NUM_GAMMA_COLS] {
let g: AB::Expr = cell.into();
builder.assert_zero(g.clone() * (AB::Expr::ONE - g.clone()) * (AB::Expr::ONE + g));
}
for col in [CELL_FLAG, CELL_B_ON] {
let f: AB::Expr = local[col].into();
builder.assert_zero(f.clone() * (AB::Expr::ONE - f));
}
let act: AB::Expr = local[COL_ACT].into();
builder.assert_zero(act.clone() * (AB::Expr::ONE - act.clone()));
let nz: AB::Expr = local[COL_NZ].into();
builder.assert_zero(nz.clone() * (AB::Expr::ONE - nz.clone()));
builder.assert_zero(
cp_sel.clone() * (AB::Expr::ONE - act.clone()) * local[TERM_CELL_MULT].into(),
);
let is_c_zero: AB::Expr = local[CELL_IS_C_ZERO].into();
let c_ptr_local: AB::Expr = local[COL_C_PTR].into();
builder.assert_zero(cp_sel.clone() * is_c_zero.clone() * c_ptr_local);
let b_ptr_local: AB::Expr = local[COL_B_PTR].into();
builder.assert_zero(ab_sel.clone() * is_b_zero.clone() * b_ptr_local);
let b_on: AB::Expr = local[CELL_B_ON].into();
builder.assert_zero(ab_sel.clone() * (b_on - act.clone() * (AB::Expr::ONE - is_b_zero)));
let c_on: AB::Expr = local[CELL_C_ON].into();
builder.assert_zero(cp_sel * (c_on - act * (AB::Expr::ONE - is_c_zero)));
let s_sum: AB::Expr =
(0..NUM_LIMBS).fold(AB::Expr::ZERO, |s, j| s + AB::Expr::from(local[CELL_HI + j]));
let w: AB::Expr = local[CELL_D_W].into();
let ws: AB::Expr = local[CELL_D_WS].into();
builder.assert_zero(ab_sel.clone() * (ws.clone() - w * s_sum));
builder.assert_zero(ab_sel.clone() * nz * (ws - AB::Expr::ONE));
for col in [COL_A_PTR, COL_B_PTR, COL_C_PTR, COL_BOUND_PTR, COL_ACT, COL_NZ] {
let here: AB::Expr = local[col].into();
let there: AB::Expr = next[col].into();
builder.assert_zero(ab_sel.clone() * (there - here));
}
let mut lb =
CyclicConstraintLookupBuilder::new(builder, self, self.preprocessed_width() > 0);
<Self as LookupAir<_>>::eval(self, &mut lb);
}
}
fn consume_column<LB>(
builder: &mut LB,
bound_ptr: &LB::Expr,
consumes: Vec<(LB::Expr, LB::Expr, [LB::Expr; 8], Deg)>,
col_deg: Deg,
) where
LB: LookupBuilder<F = Felt>,
{
builder.next_column(
|col| {
col.group(
"uintadd",
|g| {
g.batch(
"frac",
LB::Expr::ONE,
|b| {
for (mult, ptr, msg_limbs, deg) in consumes {
b.insert(
"consume-uintval",
mult,
UintValMsg {
ptr,
bound_ptr: bound_ptr.clone(),
limbs: msg_limbs,
},
deg,
);
}
},
col_deg,
);
},
col_deg,
);
},
col_deg,
);
}
impl<LB> LookupAir<LB> for UintAddAir
where
LB: LookupBuilder<F = Felt>,
{
fn num_columns(&self) -> usize {
NUM_LOGUP_COLS
}
fn column_shape(&self) -> &[usize] {
&COLUMN_SHAPE
}
fn max_message_width(&self) -> usize {
MAX_MESSAGE_WIDTH
}
fn num_bus_ids(&self) -> usize {
NUM_BUS_IDS
}
fn eval(&self, builder: &mut LB) {
let local: [LB::Var; NUM_MAIN_COLS] = current_main(builder.main(), 0);
let ab_sel: LB::Expr = builder.periodic_values()[0].into();
let cp_sel: LB::Expr = LB::Expr::ONE - ab_sel.clone();
let a_ptr: LB::Expr = local[COL_A_PTR].into();
let b_ptr: LB::Expr = local[COL_B_PTR].into();
let c_ptr: LB::Expr = local[COL_C_PTR].into();
let bound_ptr: LB::Expr = local[COL_BOUND_PTR].into();
let act: LB::Expr = local[COL_ACT].into();
let nz: LB::Expr = local[COL_NZ].into();
let neg_mult: LB::Expr = LB::Expr::ZERO - local[TERM_CELL_MULT].into();
let lo: [LB::Expr; NUM_LIMBS] = array::from_fn(|j| local[j].into());
let hi: [LB::Expr; NUM_LIMBS] = array::from_fn(|j| local[CELL_HI + j].into());
let b_on: LB::Expr = local[CELL_B_ON].into();
let c_on: LB::Expr = local[CELL_C_ON].into();
let consume_deg = Deg { v: 2, u: 1 };
let provide_deg = Deg { v: 2, u: 1 };
let cp_col_deg = Deg { v: 3, u: 2 };
let a_full = (ab_sel.clone() * act.clone(), a_ptr.clone(), lo.clone(), consume_deg);
let b_full = (ab_sel * b_on, b_ptr.clone(), hi.clone(), consume_deg);
let c_full = (cp_sel.clone() * c_on, c_ptr.clone(), lo, consume_deg);
let p_full = (cp_sel.clone() * act, bound_ptr.clone(), hi, consume_deg);
consume_column(builder, &bound_ptr, vec![a_full], consume_deg);
consume_column(builder, &bound_ptr, vec![b_full, c_full], consume_deg);
builder.next_column(
|col| {
col.group(
"uintadd-pp",
|g| {
g.batch(
"pp",
LB::Expr::ONE,
|b| {
let (mult, ptr, msg_limbs, deg) = p_full;
b.insert(
"consume-uintval",
mult,
UintValMsg {
ptr,
bound_ptr: bound_ptr.clone(),
limbs: msg_limbs,
},
deg,
);
b.insert(
"provide-uintadd",
neg_mult.clone() * cp_sel.clone(),
UintAddMsg {
bound_ptr: bound_ptr.clone(),
a_ptr: a_ptr.clone(),
b_ptr: b_ptr.clone(),
c_ptr: c_ptr.clone(),
nz: nz.clone(),
},
provide_deg,
);
},
cp_col_deg,
);
},
cp_col_deg,
);
},
cp_col_deg,
);
}
}