mod utils;
use core::borrow::{Borrow, BorrowMut};
use core::mem::size_of;
use hashbrown::HashMap;
use p3_air::{Air, AirBuilder, BaseAir};
use p3_field::AbstractField;
use p3_field::PrimeField;
use p3_matrix::dense::RowMajorMatrix;
use p3_matrix::Matrix;
use sp1_derive::AlignedBorrow;
use crate::air::MachineAir;
use crate::air::{SP1AirBuilder, Word};
use crate::alu::divrem::utils::{get_msb, get_quotient_and_remainder, is_signed_operation};
use crate::alu::{create_alu_lookups, AluEvent};
use crate::bytes::event::ByteRecord;
use crate::bytes::{ByteLookupEvent, ByteOpcode};
use crate::disassembler::WORD_SIZE;
use crate::operations::{IsEqualWordOperation, IsZeroWordOperation};
use crate::runtime::{ExecutionRecord, Opcode, Program};
use crate::utils::pad_to_power_of_two;
pub const NUM_DIVREM_COLS: usize = size_of::<DivRemCols<u8>>();
const BYTE_SIZE: usize = 8;
const LONG_WORD_SIZE: usize = 2 * WORD_SIZE;
#[derive(Default)]
pub struct DivRemChip;
#[derive(AlignedBorrow, Default, Debug, Clone, Copy)]
#[repr(C)]
pub struct DivRemCols<T> {
pub shard: T,
pub channel: T,
pub nonce: T,
pub a: Word<T>,
pub b: Word<T>,
pub c: Word<T>,
pub quotient: Word<T>,
pub remainder: Word<T>,
pub abs_remainder: Word<T>,
pub abs_c: Word<T>,
pub max_abs_c_or_1: Word<T>,
pub c_times_quotient: [T; LONG_WORD_SIZE],
pub carry: [T; LONG_WORD_SIZE],
pub is_c_0: IsZeroWordOperation<T>,
pub is_div: T,
pub is_divu: T,
pub is_rem: T,
pub is_remu: T,
pub is_overflow: T,
pub is_overflow_b: IsEqualWordOperation<T>,
pub is_overflow_c: IsEqualWordOperation<T>,
pub b_msb: T,
pub rem_msb: T,
pub c_msb: T,
pub b_neg: T,
pub rem_neg: T,
pub c_neg: T,
pub lower_nonce: T,
pub upper_nonce: T,
pub abs_nonce: T,
pub abs_c_alu_event: T,
pub abs_c_alu_event_nonce: T,
pub abs_rem_alu_event: T,
pub abs_rem_alu_event_nonce: T,
pub is_real: T,
pub remainder_check_multiplicity: T,
}
impl<F: PrimeField> MachineAir<F> for DivRemChip {
type Record = ExecutionRecord;
type Program = Program;
fn name(&self) -> String {
"DivRem".to_string()
}
fn generate_trace(
&self,
input: &ExecutionRecord,
output: &mut ExecutionRecord,
) -> RowMajorMatrix<F> {
let mut rows: Vec<[F; NUM_DIVREM_COLS]> = vec![];
let divrem_events = input.divrem_events.clone();
for event in divrem_events.iter() {
assert!(
event.opcode == Opcode::DIVU
|| event.opcode == Opcode::REMU
|| event.opcode == Opcode::REM
|| event.opcode == Opcode::DIV
);
let mut row = [F::zero(); NUM_DIVREM_COLS];
let cols: &mut DivRemCols<F> = row.as_mut_slice().borrow_mut();
{
cols.a = Word::from(event.a);
cols.b = Word::from(event.b);
cols.c = Word::from(event.c);
cols.shard = F::from_canonical_u32(event.shard);
cols.channel = F::from_canonical_u32(event.channel);
cols.is_real = F::one();
cols.is_divu = F::from_bool(event.opcode == Opcode::DIVU);
cols.is_remu = F::from_bool(event.opcode == Opcode::REMU);
cols.is_div = F::from_bool(event.opcode == Opcode::DIV);
cols.is_rem = F::from_bool(event.opcode == Opcode::REM);
cols.is_c_0.populate(event.c);
}
let (quotient, remainder) = get_quotient_and_remainder(event.b, event.c, event.opcode);
cols.quotient = Word::from(quotient);
cols.remainder = Word::from(remainder);
{
cols.rem_msb = F::from_canonical_u8(get_msb(remainder));
cols.b_msb = F::from_canonical_u8(get_msb(event.b));
cols.c_msb = F::from_canonical_u8(get_msb(event.c));
cols.is_overflow_b.populate(event.b, i32::MIN as u32);
cols.is_overflow_c.populate(event.c, -1i32 as u32);
if is_signed_operation(event.opcode) {
cols.rem_neg = cols.rem_msb;
cols.b_neg = cols.b_msb;
cols.c_neg = cols.c_msb;
cols.is_overflow =
F::from_bool(event.b as i32 == i32::MIN && event.c as i32 == -1);
cols.abs_remainder = Word::from((remainder as i32).abs() as u32);
cols.abs_c = Word::from((event.c as i32).abs() as u32);
cols.max_abs_c_or_1 = Word::from(u32::max(1, (event.c as i32).abs() as u32));
} else {
cols.abs_remainder = cols.remainder;
cols.abs_c = cols.c;
cols.max_abs_c_or_1 = Word::from(u32::max(1, event.c));
}
cols.abs_c_alu_event = cols.c_neg * cols.is_real;
cols.abs_c_alu_event_nonce = F::from_canonical_u32(
input
.nonce_lookup
.get(&event.sub_lookups[4])
.copied()
.unwrap_or_default(),
);
cols.abs_rem_alu_event = cols.rem_neg * cols.is_real;
cols.abs_rem_alu_event_nonce = F::from_canonical_u32(
input
.nonce_lookup
.get(&event.sub_lookups[5])
.copied()
.unwrap_or_default(),
);
{
let words = [event.b, event.c, remainder];
let mut blu_events: Vec<ByteLookupEvent> = vec![];
for word in words.iter() {
let most_significant_byte = word.to_le_bytes()[WORD_SIZE - 1];
blu_events.push(ByteLookupEvent {
shard: event.shard,
channel: event.channel,
opcode: ByteOpcode::MSB,
a1: get_msb(*word) as u32,
a2: 0,
b: most_significant_byte as u32,
c: 0,
});
}
output.add_byte_lookup_events(blu_events);
}
}
{
cols.remainder_check_multiplicity = cols.is_real * (F::one() - cols.is_c_0.result);
}
{
let c_times_quotient = {
if is_signed_operation(event.opcode) {
(((quotient as i32) as i64) * ((event.c as i32) as i64)).to_le_bytes()
} else {
((quotient as u64) * (event.c as u64)).to_le_bytes()
}
};
cols.c_times_quotient = c_times_quotient.map(F::from_canonical_u8);
let remainder_bytes = {
if is_signed_operation(event.opcode) {
((remainder as i32) as i64).to_le_bytes()
} else {
(remainder as u64).to_le_bytes()
}
};
let mut carry = [0u32; 8];
let base = 1 << BYTE_SIZE;
for i in 0..LONG_WORD_SIZE {
let mut x = c_times_quotient[i] as u32 + remainder_bytes[i] as u32;
if i > 0 {
x += carry[i - 1];
}
carry[i] = x / base;
cols.carry[i] = F::from_canonical_u32(carry[i]);
}
{
{
let mut add_events: Vec<AluEvent> = vec![];
if cols.abs_c_alu_event == F::one() {
add_events.push(AluEvent {
lookup_id: event.sub_lookups[4],
shard: event.shard,
channel: event.channel,
clk: event.clk,
opcode: Opcode::ADD,
a: 0,
b: event.c,
c: (event.c as i32).abs() as u32,
sub_lookups: create_alu_lookups(),
})
}
if cols.abs_rem_alu_event == F::one() {
add_events.push(AluEvent {
lookup_id: event.sub_lookups[5],
shard: event.shard,
channel: event.channel,
clk: event.clk,
opcode: Opcode::ADD,
a: 0,
b: remainder,
c: (remainder as i32).abs() as u32,
sub_lookups: create_alu_lookups(),
})
}
let mut alu_events = HashMap::new();
alu_events.insert(Opcode::ADD, add_events);
output.add_alu_events(alu_events);
}
let mut lower_word = 0;
for i in 0..WORD_SIZE {
lower_word += (c_times_quotient[i] as u32) << (i * BYTE_SIZE);
}
let mut upper_word = 0;
for i in 0..WORD_SIZE {
upper_word += (c_times_quotient[WORD_SIZE + i] as u32) << (i * BYTE_SIZE);
}
let lower_multiplication = AluEvent {
lookup_id: event.sub_lookups[0],
shard: event.shard,
channel: event.channel,
clk: event.clk,
opcode: Opcode::MUL,
a: lower_word,
c: event.c,
b: quotient,
sub_lookups: create_alu_lookups(),
};
cols.lower_nonce = F::from_canonical_u32(
input
.nonce_lookup
.get(&event.sub_lookups[0])
.copied()
.unwrap_or_default(),
);
output.add_mul_event(lower_multiplication);
let upper_multiplication = AluEvent {
lookup_id: event.sub_lookups[1],
shard: event.shard,
channel: event.channel,
clk: event.clk,
opcode: {
if is_signed_operation(event.opcode) {
Opcode::MULH
} else {
Opcode::MULHU
}
},
a: upper_word,
c: event.c,
b: quotient,
sub_lookups: create_alu_lookups(),
};
cols.upper_nonce = F::from_canonical_u32(
input
.nonce_lookup
.get(&event.sub_lookups[1])
.copied()
.unwrap_or_default(),
);
output.add_mul_event(upper_multiplication);
let lt_event = if is_signed_operation(event.opcode) {
cols.abs_nonce = F::from_canonical_u32(
input
.nonce_lookup
.get(&event.sub_lookups[2])
.copied()
.unwrap_or_default(),
);
AluEvent {
lookup_id: event.sub_lookups[2],
shard: event.shard,
channel: event.channel,
opcode: Opcode::SLTU,
a: 1,
b: (remainder as i32).abs() as u32,
c: u32::max(1, (event.c as i32).abs() as u32),
clk: event.clk,
sub_lookups: create_alu_lookups(),
}
} else {
cols.abs_nonce = F::from_canonical_u32(
input
.nonce_lookup
.get(&event.sub_lookups[3])
.copied()
.unwrap_or_default(),
);
AluEvent {
lookup_id: event.sub_lookups[3],
shard: event.shard,
channel: event.channel,
opcode: Opcode::SLTU,
a: 1,
b: remainder,
c: u32::max(1, event.c),
clk: event.clk,
sub_lookups: create_alu_lookups(),
}
};
if cols.remainder_check_multiplicity == F::one() {
output.add_lt_event(lt_event);
}
}
{
output.add_u8_range_checks(event.shard, event.channel, "ient.to_le_bytes());
output.add_u8_range_checks(
event.shard,
event.channel,
&remainder.to_le_bytes(),
);
output.add_u8_range_checks(event.shard, event.channel, &c_times_quotient);
}
}
rows.push(row);
}
let mut trace = RowMajorMatrix::new(
rows.into_iter().flatten().collect::<Vec<_>>(),
NUM_DIVREM_COLS,
);
pad_to_power_of_two::<NUM_DIVREM_COLS, F>(&mut trace.values);
let padded_row_template = {
let mut row = [F::zero(); NUM_DIVREM_COLS];
let cols: &mut DivRemCols<F> = row.as_mut_slice().borrow_mut();
cols.is_divu = F::one();
cols.c[0] = F::one();
cols.abs_c[0] = F::one();
cols.max_abs_c_or_1[0] = F::one();
cols.is_c_0.populate(1);
row
};
debug_assert!(padded_row_template.len() == NUM_DIVREM_COLS);
for i in input.divrem_events.len() * NUM_DIVREM_COLS..trace.values.len() {
trace.values[i] = padded_row_template[i % NUM_DIVREM_COLS];
}
for i in 0..trace.height() {
let cols: &mut DivRemCols<F> =
trace.values[i * NUM_DIVREM_COLS..(i + 1) * NUM_DIVREM_COLS].borrow_mut();
cols.nonce = F::from_canonical_usize(i);
}
trace
}
fn included(&self, shard: &Self::Record) -> bool {
!shard.divrem_events.is_empty()
}
}
impl<F> BaseAir<F> for DivRemChip {
fn width(&self) -> usize {
NUM_DIVREM_COLS
}
}
impl<AB> Air<AB> for DivRemChip
where
AB: SP1AirBuilder,
{
fn eval(&self, builder: &mut AB) {
let main = builder.main();
let local = main.row_slice(0);
let local: &DivRemCols<AB::Var> = (*local).borrow();
let next = main.row_slice(1);
let next: &DivRemCols<AB::Var> = (*next).borrow();
let base = AB::F::from_canonical_u32(1 << 8);
let one: AB::Expr = AB::F::one().into();
let zero: AB::Expr = AB::F::zero().into();
builder.when_first_row().assert_zero(local.nonce);
builder
.when_transition()
.assert_eq(local.nonce + AB::Expr::one(), next.nonce);
{
let is_signed_type = local.is_div + local.is_rem;
let msb_sign_pairs = [
(local.b_msb, local.b_neg),
(local.rem_msb, local.rem_neg),
(local.c_msb, local.c_neg),
];
for msb_sign_pair in msb_sign_pairs.iter() {
let msb = msb_sign_pair.0;
let is_negative = msb_sign_pair.1;
builder.assert_eq(msb * is_signed_type.clone(), is_negative);
}
}
{
let lower_half: [AB::Expr; 4] = [
local.c_times_quotient[0].into(),
local.c_times_quotient[1].into(),
local.c_times_quotient[2].into(),
local.c_times_quotient[3].into(),
];
builder.send_alu(
AB::Expr::from_canonical_u32(Opcode::MUL as u32),
Word(lower_half),
local.quotient,
local.c,
local.shard,
local.channel,
local.lower_nonce,
local.is_real,
);
let opcode_for_upper_half = {
let mulh = AB::Expr::from_canonical_u32(Opcode::MULH as u32);
let mulhu = AB::Expr::from_canonical_u32(Opcode::MULHU as u32);
let is_signed = local.is_div + local.is_rem;
let is_unsigned = local.is_divu + local.is_remu;
is_signed * mulh + is_unsigned * mulhu
};
let upper_half: [AB::Expr; 4] = [
local.c_times_quotient[4].into(),
local.c_times_quotient[5].into(),
local.c_times_quotient[6].into(),
local.c_times_quotient[7].into(),
];
builder.send_alu(
opcode_for_upper_half,
Word(upper_half),
local.quotient,
local.c,
local.shard,
local.channel,
local.upper_nonce,
local.is_real,
);
}
{
IsEqualWordOperation::<AB::F>::eval(
builder,
local.b.map(|x| x.into()),
Word::from(i32::MIN as u32).map(|x: AB::F| x.into()),
local.is_overflow_b,
local.is_real.into(),
);
IsEqualWordOperation::<AB::F>::eval(
builder,
local.c.map(|x| x.into()),
Word::from(-1i32 as u32).map(|x: AB::F| x.into()),
local.is_overflow_c,
local.is_real.into(),
);
let is_signed = local.is_div + local.is_rem;
builder.assert_eq(
local.is_overflow,
local.is_overflow_b.is_diff_zero.result
* local.is_overflow_c.is_diff_zero.result
* is_signed,
);
}
{
let sign_extension = local.rem_neg * AB::F::from_canonical_u8(u8::MAX);
let mut c_times_quotient_plus_remainder: Vec<AB::Expr> =
vec![AB::F::zero().into(); LONG_WORD_SIZE];
for i in 0..LONG_WORD_SIZE {
c_times_quotient_plus_remainder[i] = local.c_times_quotient[i].into();
if i < WORD_SIZE {
c_times_quotient_plus_remainder[i] += local.remainder[i].into();
} else {
c_times_quotient_plus_remainder[i] += sign_extension.clone();
}
c_times_quotient_plus_remainder[i] -= local.carry[i] * base;
if i > 0 {
c_times_quotient_plus_remainder[i] += local.carry[i - 1].into();
}
}
for i in 0..LONG_WORD_SIZE {
if i < WORD_SIZE {
builder.assert_eq(local.b[i], c_times_quotient_plus_remainder[i].clone());
} else {
let not_overflow = one.clone() - local.is_overflow;
builder
.when(not_overflow.clone())
.when(local.b_neg)
.assert_eq(
c_times_quotient_plus_remainder[i].clone(),
AB::F::from_canonical_u8(u8::MAX),
);
builder
.when(not_overflow.clone())
.when_ne(one.clone(), local.b_neg)
.assert_zero(c_times_quotient_plus_remainder[i].clone());
builder
.when(local.is_overflow)
.assert_zero(c_times_quotient_plus_remainder[i].clone());
}
}
}
for i in 0..WORD_SIZE {
builder
.when(local.is_divu + local.is_div)
.assert_eq(local.quotient[i], local.a[i]);
builder
.when(local.is_remu + local.is_rem)
.assert_eq(local.remainder[i], local.a[i]);
}
{
let mut rem_byte_sum = zero.clone();
let mut b_byte_sum = zero.clone();
for i in 0..WORD_SIZE {
rem_byte_sum += local.remainder[i].into();
b_byte_sum += local.b[i].into();
}
builder
.when(local.rem_neg) .assert_one(local.b_neg);
builder
.when(rem_byte_sum.clone()) .when(one.clone() - local.rem_neg) .assert_zero(local.b_neg); }
{
IsZeroWordOperation::<AB::F>::eval(
builder,
local.c.map(|x| x.into()),
local.is_c_0,
local.is_real.into(),
);
for i in 0..WORD_SIZE {
builder
.when(local.is_c_0.result)
.when(local.is_divu + local.is_div)
.assert_eq(local.quotient[i], AB::F::from_canonical_u8(u8::MAX));
}
}
{
for i in 0..WORD_SIZE {
builder
.when_not(local.c_neg)
.assert_eq(local.c[i], local.abs_c[i]);
builder
.when_not(local.rem_neg)
.assert_eq(local.remainder[i], local.abs_remainder[i]);
}
builder.send_alu(
AB::Expr::from_canonical_u32(Opcode::ADD as u32),
Word([zero.clone(), zero.clone(), zero.clone(), zero.clone()]),
local.c,
local.abs_c,
local.shard,
local.channel,
local.abs_c_alu_event_nonce,
local.abs_c_alu_event,
);
builder.send_alu(
AB::Expr::from_canonical_u32(Opcode::ADD as u32),
Word([zero.clone(), zero.clone(), zero.clone(), zero.clone()]),
local.remainder,
local.abs_remainder,
local.shard,
local.channel,
local.abs_rem_alu_event_nonce,
local.abs_rem_alu_event,
);
let max_abs_c_or_1: Word<AB::Expr> = {
let mut v = vec![zero.clone(); WORD_SIZE];
v[0] = local.is_c_0.result * one.clone()
+ (one.clone() - local.is_c_0.result) * local.abs_c[0];
for i in 1..WORD_SIZE {
v[i] = (one.clone() - local.is_c_0.result) * local.abs_c[i];
}
Word(v.try_into().unwrap_or_else(|_| panic!("Incorrect length")))
};
for i in 0..WORD_SIZE {
builder.assert_eq(local.max_abs_c_or_1[i], max_abs_c_or_1[i].clone());
}
builder.assert_eq(
(AB::Expr::one() - local.is_c_0.result) * local.is_real,
local.remainder_check_multiplicity,
);
builder.assert_eq(local.abs_c_alu_event, local.c_neg * local.is_real);
builder.assert_eq(local.abs_rem_alu_event, local.rem_neg * local.is_real);
builder.send_alu(
AB::Expr::from_canonical_u32(Opcode::SLTU as u32),
Word([one.clone(), zero.clone(), zero.clone(), zero.clone()]),
local.abs_remainder,
local.max_abs_c_or_1,
local.shard,
local.channel,
local.abs_nonce,
local.remainder_check_multiplicity,
);
}
{
let msb_pairs = [
(local.b_msb, local.b[WORD_SIZE - 1]),
(local.c_msb, local.c[WORD_SIZE - 1]),
(local.rem_msb, local.remainder[WORD_SIZE - 1]),
];
let opcode = AB::F::from_canonical_u32(ByteOpcode::MSB as u32);
for msb_pair in msb_pairs.iter() {
let msb = msb_pair.0;
let byte = msb_pair.1;
builder.send_byte(
opcode,
msb,
byte,
zero.clone(),
local.shard,
local.channel,
local.is_real,
);
}
}
{
builder.slice_range_check_u8(
&local.quotient.0,
local.shard,
local.channel,
local.is_real,
);
builder.slice_range_check_u8(
&local.remainder.0,
local.shard,
local.channel,
local.is_real,
);
local.carry.iter().for_each(|carry| {
builder.assert_bool(*carry);
});
builder.slice_range_check_u8(
&local.c_times_quotient,
local.shard,
local.channel,
local.is_real,
);
}
{
let bool_flags = [
local.is_div,
local.is_divu,
local.is_rem,
local.is_remu,
local.is_overflow,
local.b_msb,
local.rem_msb,
local.c_msb,
local.b_neg,
local.rem_neg,
local.c_neg,
local.is_real,
local.abs_c_alu_event,
local.abs_rem_alu_event,
];
for flag in bool_flags.iter() {
builder.assert_bool(*flag);
}
}
{
builder.assert_eq(
one.clone(),
local.is_divu + local.is_remu + local.is_div + local.is_rem,
);
let opcode = {
let divu: AB::Expr = AB::F::from_canonical_u32(Opcode::DIVU as u32).into();
let remu: AB::Expr = AB::F::from_canonical_u32(Opcode::REMU as u32).into();
let div: AB::Expr = AB::F::from_canonical_u32(Opcode::DIV as u32).into();
let rem: AB::Expr = AB::F::from_canonical_u32(Opcode::REM as u32).into();
local.is_divu * divu
+ local.is_remu * remu
+ local.is_div * div
+ local.is_rem * rem
};
builder.receive_alu(
opcode,
local.a,
local.b,
local.c,
local.shard,
local.channel,
local.nonce,
local.is_real,
);
}
}
}
#[cfg(test)]
mod tests {
use crate::{
air::MachineAir,
stark::StarkGenericConfig,
utils::{uni_stark_prove as prove, uni_stark_verify as verify},
};
use p3_baby_bear::BabyBear;
use p3_matrix::dense::RowMajorMatrix;
use crate::{
alu::AluEvent,
runtime::{ExecutionRecord, Opcode},
utils::BabyBearPoseidon2,
};
use super::DivRemChip;
#[test]
fn generate_trace() {
let mut shard = ExecutionRecord::default();
shard.divrem_events = vec![AluEvent::new(0, 0, 0, Opcode::DIVU, 2, 17, 3)];
let chip = DivRemChip::default();
let trace: RowMajorMatrix<BabyBear> =
chip.generate_trace(&shard, &mut ExecutionRecord::default());
println!("{:?}", trace.values)
}
fn neg(a: u32) -> u32 {
u32::MAX - a + 1
}
#[test]
fn prove_babybear() {
let config = BabyBearPoseidon2::new();
let mut challenger = config.challenger();
let mut divrem_events: Vec<AluEvent> = Vec::new();
let divrems: Vec<(Opcode, u32, u32, u32)> = vec![
(Opcode::DIVU, 3, 20, 6),
(Opcode::DIVU, 715827879, neg(20), 6),
(Opcode::DIVU, 0, 20, neg(6)),
(Opcode::DIVU, 0, neg(20), neg(6)),
(Opcode::DIVU, 1 << 31, 1 << 31, 1),
(Opcode::DIVU, 0, 1 << 31, neg(1)),
(Opcode::DIVU, u32::MAX, 1 << 31, 0),
(Opcode::DIVU, u32::MAX, 1, 0),
(Opcode::DIVU, u32::MAX, 0, 0),
(Opcode::REMU, 4, 18, 7),
(Opcode::REMU, 6, neg(20), 11),
(Opcode::REMU, 23, 23, neg(6)),
(Opcode::REMU, neg(21), neg(21), neg(11)),
(Opcode::REMU, 5, 5, 0),
(Opcode::REMU, neg(1), neg(1), 0),
(Opcode::REMU, 0, 0, 0),
(Opcode::REM, 7, 16, 9),
(Opcode::REM, neg(4), neg(22), 6),
(Opcode::REM, 1, 25, neg(3)),
(Opcode::REM, neg(2), neg(22), neg(4)),
(Opcode::REM, 0, 873, 1),
(Opcode::REM, 0, 873, neg(1)),
(Opcode::REM, 5, 5, 0),
(Opcode::REM, neg(5), neg(5), 0),
(Opcode::REM, 0, 0, 0),
(Opcode::REM, 0, 0x80000001, neg(1)),
(Opcode::DIV, 3, 18, 6),
(Opcode::DIV, neg(6), neg(24), 4),
(Opcode::DIV, neg(2), 16, neg(8)),
(Opcode::DIV, neg(1), 0, 0),
(Opcode::DIV, 1 << 31, 1 << 31, neg(1)),
(Opcode::REM, 0, 1 << 31, neg(1)),
];
for t in divrems.iter() {
divrem_events.push(AluEvent::new(0, 9, 0, t.0, t.1, t.2, t.3));
}
for _ in 0..(1000 - divrems.len()) {
divrem_events.push(AluEvent::new(0, 0, 0, Opcode::DIVU, 1, 1, 1));
}
let mut shard = ExecutionRecord::default();
shard.divrem_events = divrem_events;
let chip = DivRemChip::default();
let trace: RowMajorMatrix<BabyBear> =
chip.generate_trace(&shard, &mut ExecutionRecord::default());
let proof = prove::<BabyBearPoseidon2, _>(&config, &chip, &mut challenger, trace);
let mut challenger = config.challenger();
verify(&config, &chip, &mut challenger, &proof).unwrap();
}
}