Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

chore: remove temporary allocations from num_bits #6600

Merged
merged 7 commits into from
Nov 25, 2024
Merged
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
57 changes: 36 additions & 21 deletions acvm-repo/acir_field/src/field_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,23 +158,6 @@ impl<F: PrimeField> FieldElement<F> {
let fr = F::from_str(input).ok()?;
Some(FieldElement(fr))
}

fn bits(&self) -> Vec<bool> {
fn byte_to_bit(byte: u8) -> Vec<bool> {
let mut bits = Vec::with_capacity(8);
for index in (0..=7).rev() {
bits.push((byte & (1 << index)) >> index == 1);
}
bits
}

let bytes = self.to_be_bytes();
let mut bits = Vec::with_capacity(bytes.len() * 8);
for byte in bytes {
bits.extend(byte_to_bit(byte));
}
bits
}
}

impl<F: PrimeField> AcirField for FieldElement<F> {
Expand Down Expand Up @@ -224,12 +207,26 @@ impl<F: PrimeField> AcirField for FieldElement<F> {

/// This is the number of bits required to represent this specific field element
fn num_bits(&self) -> u32 {
let bits = self.bits();
// Iterate the number of bits and pop off all leading zeroes
let iter = bits.iter().skip_while(|x| !(**x));
let bytes = self.to_be_bytes();

// Iterate through the byte decomposition and pop off all leading zeroes
let mut iter = bytes.iter().skip_while(|x| (**x) == 0);

// The first non-zero byte in the decomposition may have some leading zero-bits.
let Some(head_byte) = iter.next() else {
// If we don't have a non-zero byte then the field element is zero,
// which we consider to require a single bit to represent.
return 1;
};
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
let num_bits_for_head_byte = head_byte.ilog2();

// Each remaining byte in the byte decomposition requires 8 bits.
//
// Note: count will panic if it goes over usize::MAX.
// This may not be suitable for devices whose usize < u16
iter.count() as u32
let tail_length = iter.count() as u32;

8 * tail_length + num_bits_for_head_byte + 1
}

fn to_u128(self) -> u128 {
Expand Down Expand Up @@ -374,6 +371,24 @@ mod tests {
use super::{AcirField, FieldElement};
use proptest::prelude::*;

proptest! {
TomAFrench marked this conversation as resolved.
Show resolved Hide resolved
#[test]
fn num_bits_agrees_with_ilog2(num in 1u128..) {
let field = FieldElement::<ark_bn254::Fr>::from(num);
prop_assert_eq!(field.num_bits(), num.ilog2() + 1);
}
}

#[test]
fn test_fits_in_u128() {
let field = FieldElement::<ark_bn254::Fr>::from(u128::MAX);
assert_eq!(field.num_bits(), 128);
assert!(field.fits_in_u128());
let big_field = field + FieldElement::one();
assert_eq!(big_field.num_bits(), 129);
assert!(!big_field.fits_in_u128());
}

#[test]
fn serialize_fixed_test_vectors() {
// Serialized field elements from of 0, -1, -2, -3
Expand Down
Loading