use core::borrow::{Borrow, BorrowMut};
use core::mem::size_of;
use p3_air::{Air, AirBuilder, AirBuilderWithPublicValues, BaseAir, PairBuilder};
use p3_field::AbstractField;
use p3_field::PrimeField;
use p3_matrix::dense::RowMajorMatrix;
use p3_matrix::Matrix;
use sp1_derive::AlignedBorrow;
use crate::air::{AirInteraction, PublicValues, SP1AirBuilder, SP1_PROOF_NUM_PV_ELTS};
use crate::air::{MachineAir, Word};
use crate::operations::IsZeroOperation;
use crate::runtime::{ExecutionRecord, Program};
use crate::utils::pad_to_power_of_two;
pub const NUM_MEMORY_PROGRAM_PREPROCESSED_COLS: usize =
size_of::<MemoryProgramPreprocessedCols<u8>>();
pub const NUM_MEMORY_PROGRAM_MULT_COLS: usize = size_of::<MemoryProgramMultCols<u8>>();
#[derive(AlignedBorrow, Clone, Copy, Default)]
#[repr(C)]
pub struct MemoryProgramPreprocessedCols<T> {
pub addr: T,
pub value: Word<T>,
pub is_real: T,
}
#[derive(AlignedBorrow, Clone, Copy, Default)]
#[repr(C)]
pub struct MemoryProgramMultCols<T> {
pub multiplicity: T,
pub is_first_shard: IsZeroOperation<T>,
}
#[derive(Default)]
pub struct MemoryProgramChip;
impl MemoryProgramChip {
pub const fn new() -> Self {
Self {}
}
}
impl<F: PrimeField> MachineAir<F> for MemoryProgramChip {
type Record = ExecutionRecord;
type Program = Program;
fn name(&self) -> String {
"MemoryProgram".to_string()
}
fn preprocessed_width(&self) -> usize {
NUM_MEMORY_PROGRAM_PREPROCESSED_COLS
}
fn generate_preprocessed_trace(&self, program: &Self::Program) -> Option<RowMajorMatrix<F>> {
let program_memory = program.memory_image.clone();
let rows = program_memory
.into_iter()
.map(|(addr, word)| {
let mut row = [F::zero(); NUM_MEMORY_PROGRAM_PREPROCESSED_COLS];
let cols: &mut MemoryProgramPreprocessedCols<F> = row.as_mut_slice().borrow_mut();
cols.addr = F::from_canonical_u32(addr);
cols.value = Word::from(word);
cols.is_real = F::one();
row
})
.collect::<Vec<_>>();
let mut trace = RowMajorMatrix::new(
rows.into_iter().flatten().collect::<Vec<_>>(),
NUM_MEMORY_PROGRAM_PREPROCESSED_COLS,
);
pad_to_power_of_two::<NUM_MEMORY_PROGRAM_PREPROCESSED_COLS, F>(&mut trace.values);
Some(trace)
}
fn generate_dependencies(&self, _input: &ExecutionRecord, _output: &mut ExecutionRecord) {
}
fn generate_trace(
&self,
input: &ExecutionRecord,
_output: &mut ExecutionRecord,
) -> RowMajorMatrix<F> {
let program_memory_addrs = input
.program
.memory_image
.keys()
.copied()
.collect::<Vec<_>>();
let mult = if input.public_values.shard == 1 {
F::one()
} else {
F::zero()
};
let rows = program_memory_addrs
.into_iter()
.map(|_| {
let mut row = [F::zero(); NUM_MEMORY_PROGRAM_MULT_COLS];
let cols: &mut MemoryProgramMultCols<F> = row.as_mut_slice().borrow_mut();
cols.multiplicity = mult;
cols.is_first_shard.populate(input.public_values.shard - 1);
row
})
.collect::<Vec<_>>();
let mut trace = RowMajorMatrix::new(
rows.into_iter().flatten().collect::<Vec<_>>(),
NUM_MEMORY_PROGRAM_MULT_COLS,
);
pad_to_power_of_two::<NUM_MEMORY_PROGRAM_MULT_COLS, F>(&mut trace.values);
trace
}
fn included(&self, _: &Self::Record) -> bool {
true
}
}
impl<F> BaseAir<F> for MemoryProgramChip {
fn width(&self) -> usize {
NUM_MEMORY_PROGRAM_MULT_COLS
}
}
impl<AB> Air<AB> for MemoryProgramChip
where
AB: SP1AirBuilder + PairBuilder + AirBuilderWithPublicValues,
{
fn eval(&self, builder: &mut AB) {
let preprocessed = builder.preprocessed();
let main = builder.main();
let prep_local = preprocessed.row_slice(0);
let prep_local: &MemoryProgramPreprocessedCols<AB::Var> = (*prep_local).borrow();
let mult_local = main.row_slice(0);
let mult_local: &MemoryProgramMultCols<AB::Var> = (*mult_local).borrow();
let public_values_slice: [AB::Expr; SP1_PROOF_NUM_PV_ELTS] =
core::array::from_fn(|i| builder.public_values()[i].into());
let public_values: &PublicValues<Word<AB::Expr>, AB::Expr> =
public_values_slice.as_slice().borrow();
IsZeroOperation::<AB::F>::eval(
builder,
public_values.shard.clone() - AB::F::one(),
mult_local.is_first_shard,
prep_local.is_real.into(),
);
builder.assert_bool(mult_local.multiplicity);
builder
.when(mult_local.is_first_shard.result)
.assert_eq(mult_local.multiplicity, prep_local.is_real.into());
builder
.when_not(mult_local.is_first_shard.result)
.assert_zero(mult_local.multiplicity);
let mut values = vec![AB::Expr::zero(), AB::Expr::zero(), prep_local.addr.into()];
values.extend(prep_local.value.map(Into::into));
builder.receive(AirInteraction::new(
values,
mult_local.multiplicity.into(),
crate::lookup::InteractionKind::Memory,
));
}
}