use crate::operations::U32toU8Operation;
use slop_algebra::{AbstractField, Field};
use sp1_core_executor::{
events::{ByteLookupEvent, ByteRecord},
ByteOpcode,
};
use sp1_derive::AlignedBorrow;
use sp1_hypercube::air::SP1AirBuilder;
#[derive(AlignedBorrow, Default, Debug, Clone, Copy)]
#[repr(C)]
pub struct AndU32Operation<T> {
pub b_low_bytes: U32toU8Operation<T>,
pub c_low_bytes: U32toU8Operation<T>,
pub value: [T; 4],
}
impl<F: Field> AndU32Operation<F> {
pub fn populate_and_u32(
&mut self,
record: &mut impl ByteRecord,
b_u32: u32,
c_u32: u32,
) -> u32 {
let expected = b_u32 & c_u32;
self.b_low_bytes.populate_u32_to_u8_unsafe(b_u32);
self.c_low_bytes.populate_u32_to_u8_unsafe(c_u32);
let b_bytes = b_u32.to_le_bytes();
let c_bytes = c_u32.to_le_bytes();
for i in 0..4 {
let and = b_bytes[i] & c_bytes[i];
self.value[i] = F::from_canonical_u8(and);
let byte_event = ByteLookupEvent {
opcode: ByteOpcode::AND,
a: and as u16,
b: b_bytes[i],
c: c_bytes[i],
};
record.add_byte_lookup_event(byte_event);
}
expected
}
pub fn eval_and_u32<AB: SP1AirBuilder>(
builder: &mut AB,
b: [AB::Expr; 2],
c: [AB::Expr; 2],
cols: AndU32Operation<AB::Var>,
is_real: AB::Var,
) -> [AB::Expr; 2] {
builder.assert_bool(is_real);
let b_bytes =
U32toU8Operation::<AB::F>::eval_u32_to_u8_unsafe(builder, b, cols.b_low_bytes);
let c_bytes =
U32toU8Operation::<AB::F>::eval_u32_to_u8_unsafe(builder, c, cols.c_low_bytes);
for i in 0..4 {
builder.send_byte(
AB::F::from_canonical_u32(ByteOpcode::AND as u32),
cols.value[i],
b_bytes[i].clone(),
c_bytes[i].clone(),
is_real,
);
}
let result_limb0 = cols.value[0] + cols.value[1] * AB::F::from_canonical_u32(1 << 8);
let result_limb1 = cols.value[2] + cols.value[3] * AB::F::from_canonical_u32(1 << 8);
[result_limb0, result_limb1]
}
}