acvm_blackbox_solver/
logic.rsuse acir::AcirField;
pub fn bit_and<F: AcirField>(lhs: F, rhs: F, num_bits: u32) -> F {
bitwise_op(lhs, rhs, num_bits, |lhs_byte, rhs_byte| lhs_byte & rhs_byte)
}
pub fn bit_xor<F: AcirField>(lhs: F, rhs: F, num_bits: u32) -> F {
bitwise_op(lhs, rhs, num_bits, |lhs_byte, rhs_byte| lhs_byte ^ rhs_byte)
}
fn bitwise_op<F: AcirField>(lhs: F, rhs: F, num_bits: u32, op: fn(u8, u8) -> u8) -> F {
let lhs_bytes = mask_to_le_bytes(lhs, num_bits);
let rhs_bytes = mask_to_le_bytes(rhs, num_bits);
let and_byte_arr: Vec<_> =
lhs_bytes.into_iter().zip(rhs_bytes).map(|(left, right)| op(left, right)).collect();
F::from_le_bytes_reduce(&and_byte_arr)
}
fn mask_to_le_bytes<F: AcirField>(field: F, num_bits: u32) -> Vec<u8> {
let mut bytes = field.to_le_bytes();
mask_vector_le(&mut bytes, num_bits as usize);
bytes
}
fn mask_vector_le(bytes: &mut [u8], num_bits: usize) {
let total_bits = bytes.len() * 8;
if num_bits >= total_bits {
return;
}
let array_mask_index = num_bits / 8;
let mask_power = num_bits % 8;
bytes[array_mask_index] &= 2u8.pow(mask_power as u32) - 1;
for byte in &mut bytes[(array_mask_index + 1)..] {
*byte = 0;
}
}
#[cfg(test)]
mod tests {
use acir::FieldElement;
use proptest::prelude::*;
use crate::{bit_and, bit_xor};
proptest! {
#[test]
fn matches_bitwise_and_on_u128s(x in 0..=u128::MAX, y in 0..=u128::MAX, bit_size in 128u32..) {
let x_as_field = FieldElement::from(x);
let y_as_field = FieldElement::from(y);
let x_and_y = x & y;
let x_and_y_as_field = bit_and(x_as_field, y_as_field, bit_size);
prop_assert_eq!(x_and_y_as_field, FieldElement::from(x_and_y), "AND on fields should match that on integers");
}
#[test]
fn matches_bitwise_xor_on_u128s(x in 0..=u128::MAX, y in 0..=u128::MAX, bit_size in 128u32..) {
let x_as_field = FieldElement::from(x);
let y_as_field = FieldElement::from(y);
let x_xor_y = x ^ y;
let x_xor_y_as_field = bit_xor(x_as_field, y_as_field, bit_size);
prop_assert_eq!(x_xor_y_as_field, FieldElement::from(x_xor_y), "XOR on fields should match that on integers");
}
}
}