use alloc::vec::Vec;
use core::borrow::BorrowMut;
use miden_air::{
BitwiseCols,
trace::chiplets::bitwise::{BITWISE_AND, BITWISE_XOR, OP_CYCLE_LEN, TRACE_WIDTH},
};
use crate::{Felt, ZERO, operation::OperationError, trace::ChipletTraceFragment};
#[cfg(test)]
mod tests;
const INIT_OPS_CAPACITY: usize = 128;
#[derive(Debug, Clone, Copy)]
enum Op {
And,
Xor,
}
impl Op {
fn selector(self) -> Felt {
match self {
Self::And => BITWISE_AND,
Self::Xor => BITWISE_XOR,
}
}
fn apply(self, a: u32, b: u32) -> u32 {
match self {
Self::And => a & b,
Self::Xor => a ^ b,
}
}
}
#[derive(Debug, Clone, Copy)]
struct BitwiseOp {
op: Op,
a: u32,
b: u32,
}
#[derive(Debug)]
pub struct Bitwise {
ops: Vec<BitwiseOp>,
}
impl Bitwise {
pub fn new() -> Self {
Self {
ops: Vec::with_capacity(INIT_OPS_CAPACITY),
}
}
pub fn trace_len(&self) -> usize {
self.ops.len() * OP_CYCLE_LEN
}
pub fn u32and(&mut self, a: Felt, b: Felt) -> Result<Felt, OperationError> {
self.record(Op::And, a, b)
}
pub fn u32xor(&mut self, a: Felt, b: Felt) -> Result<Felt, OperationError> {
self.record(Op::Xor, a, b)
}
fn record(&mut self, op: Op, a: Felt, b: Felt) -> Result<Felt, OperationError> {
let a = assert_u32(a)?;
let b = assert_u32(b)?;
self.ops.push(BitwiseOp { op, a, b });
Ok(Felt::from_u32(op.apply(a, b)))
}
pub fn fill_trace(self, trace: &mut ChipletTraceFragment) {
debug_assert_eq!(self.trace_len(), trace.len(), "inconsistent trace lengths");
debug_assert_eq!(TRACE_WIDTH, trace.width(), "inconsistent trace widths");
let mut chunk = [ZERO; TRACE_WIDTH * OP_CYCLE_LEN];
for (op_idx, &BitwiseOp { op, a, b }) in self.ops.iter().enumerate() {
let (chunk_rows, _) = chunk.as_mut_slice().as_chunks_mut::<TRACE_WIDTH>();
let a = a as u64;
let b = b as u64;
let selector = op.selector();
let mut result: u64 = 0;
for (i, bit_offset) in (0..32).step_by(4).rev().enumerate() {
let prev_output = result;
let a_acc = a >> bit_offset;
let b_acc = b >> bit_offset;
let result_4_bit = match op {
Op::And => (a_acc & b_acc) & 0xf,
Op::Xor => (a_acc ^ b_acc) & 0xf,
};
result = (result << 4) | result_4_bit;
let cols: &mut BitwiseCols<Felt> = chunk_rows[i].as_mut_slice().borrow_mut();
cols.op_flag = selector;
cols.a = Felt::new_unchecked(a_acc);
cols.b = Felt::new_unchecked(b_acc);
cols.a_bits = [
Felt::new_unchecked(a_acc & 1),
Felt::new_unchecked((a_acc >> 1) & 1),
Felt::new_unchecked((a_acc >> 2) & 1),
Felt::new_unchecked((a_acc >> 3) & 1),
];
cols.b_bits = [
Felt::new_unchecked(b_acc & 1),
Felt::new_unchecked((b_acc >> 1) & 1),
Felt::new_unchecked((b_acc >> 2) & 1),
Felt::new_unchecked((b_acc >> 3) & 1),
];
cols.prev_output = Felt::new_unchecked(prev_output);
cols.output = Felt::new_unchecked(result);
}
trace.copy_rows_into(op_idx * OP_CYCLE_LEN, &chunk);
}
}
}
impl Default for Bitwise {
fn default() -> Self {
Self::new()
}
}
pub fn assert_u32(value: Felt) -> Result<u32, OperationError> {
u32::try_from(value.as_canonical_u64())
.map_err(|_| OperationError::NotU32Values { values: vec![value] })
}