use crate::{integer::Integer, integers::uint::*, ToBytesGadget};
use snarkvm_fields::Field;
use snarkvm_r1cs::{ConstraintSystem, SynthesisError};
macro_rules! to_bytes_impl {
($gadget: ident, $size: expr) => {
impl<F: Field> ToBytesGadget<F> for $gadget {
#[inline]
fn to_bytes<CS: ConstraintSystem<F>>(&self, _cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
let bits = self.to_bits_le();
debug_assert_eq!($size, bits.len());
let value_bytes = self.value.map_or(vec![None; $size / 8], |value| {
value.to_le_bytes().iter().map(|byte| Some(*byte)).collect()
});
Ok(bits
.chunks(8)
.into_iter()
.zip(value_bytes)
.map(|(chunk8, value)| UInt8 {
bits: chunk8.to_vec(),
negated: false,
value,
})
.collect())
}
fn to_bytes_strict<CS: ConstraintSystem<F>>(&self, cs: CS) -> Result<Vec<UInt8>, SynthesisError> {
self.to_bytes(cs)
}
}
};
}
to_bytes_impl!(UInt8, 8);
to_bytes_impl!(UInt16, 16);
to_bytes_impl!(UInt32, 32);
to_bytes_impl!(UInt64, 64);
to_bytes_impl!(UInt128, 128);
#[cfg(test)]
mod tests {
use super::*;
use snarkvm_curves::bls12_377::Fr;
use snarkvm_r1cs::{ConstraintSystem, TestConstraintSystem};
use num_traits::PrimInt;
use rand::{thread_rng, Rng};
const ITERATIONS: usize = 100_000;
#[test]
fn test_uint_to_bytes() {
fn uint_to_bytes_test<F: Field, U: PrimInt, UInt: Integer<IntegerType = U> + ToBytesGadget<F>>(
expected: U,
expected_bytes: &[u8],
) {
let mut cs = TestConstraintSystem::<F>::new();
let candidate = UInt::constant(expected);
let candidate_bytes = candidate.to_bytes(cs.ns(|| "to_bytes")).unwrap();
assert_eq!(expected_bytes.len(), candidate_bytes.len());
for (expected_byte, candidate_byte) in expected_bytes.iter().zip(candidate_bytes) {
println!("{} == {}", expected_byte, candidate_byte.value.unwrap());
assert_eq!(*expected_byte, candidate_byte.value.unwrap());
}
}
for _ in 0..ITERATIONS {
let expected: u8 = thread_rng().gen();
uint_to_bytes_test::<Fr, u8, UInt8>(expected, &expected.to_le_bytes());
let expected: u16 = thread_rng().gen();
uint_to_bytes_test::<Fr, u16, UInt16>(expected, &expected.to_le_bytes());
let expected: u32 = thread_rng().gen();
uint_to_bytes_test::<Fr, u32, UInt32>(expected, &expected.to_le_bytes());
let expected: u64 = thread_rng().gen();
uint_to_bytes_test::<Fr, u64, UInt64>(expected, &expected.to_le_bytes());
let expected: u128 = thread_rng().gen();
uint_to_bytes_test::<Fr, u128, UInt128>(expected, &expected.to_le_bytes());
}
}
}