diff --git a/acvm-repo/acir_field/src/field_element.rs b/acvm-repo/acir_field/src/field_element.rs index 52bf7b2996b..8096fdb17c9 100644 --- a/acvm-repo/acir_field/src/field_element.rs +++ b/acvm-repo/acir_field/src/field_element.rs @@ -367,9 +367,9 @@ struct BitCounter { impl BitCounter { fn bits(&self) -> u32 { // 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. + // which we consider to require a zero bits to represent. if self.count == 0 { - return 1; + return 0; } let num_bits_for_head_byte = self.head_byte.ilog2(); @@ -406,8 +406,14 @@ mod tests { use proptest::prelude::*; #[test] - fn requires_one_bit_to_hold_zero() { + fn requires_zero_bit_to_hold_zero() { let field = FieldElement::::zero(); + assert_eq!(field.num_bits(), 0); + } + + #[test] + fn requires_one_bit_to_hold_one() { + let field = FieldElement::::one(); assert_eq!(field.num_bits(), 1); } diff --git a/acvm-repo/acvm/src/pwg/blackbox/range.rs b/acvm-repo/acvm/src/pwg/blackbox/range.rs index 039a04b9063..67d44b21f3f 100644 --- a/acvm-repo/acvm/src/pwg/blackbox/range.rs +++ b/acvm-repo/acvm/src/pwg/blackbox/range.rs @@ -27,7 +27,7 @@ mod tests { use std::collections::BTreeMap; use acir::{ - FieldElement, + AcirField, FieldElement, circuit::opcodes::FunctionInput, native_types::{Witness, WitnessMap}, }; @@ -42,6 +42,13 @@ mod tests { assert!(solve_range_opcode(&witness_map, &input, false).is_err()); } + #[test] + fn accepts_zero_for_zero_bits() { + let witness_map = WitnessMap::from(BTreeMap::from([(Witness(0), FieldElement::zero())])); + let input: FunctionInput = FunctionInput::witness(Witness(0), 0); + assert!(solve_range_opcode(&witness_map, &input, false).is_ok()); + } + #[test] fn accepts_valid_inputs() { let values: [u32; 4] = [0, 1, 8, 255]; diff --git a/compiler/noirc_evaluator/src/acir/acir_context/mod.rs b/compiler/noirc_evaluator/src/acir/acir_context/mod.rs index f2f71f40874..e4a83a4154d 100644 --- a/compiler/noirc_evaluator/src/acir/acir_context/mod.rs +++ b/compiler/noirc_evaluator/src/acir/acir_context/mod.rs @@ -921,6 +921,14 @@ impl> AcirContext { // `rhs` has the correct bit-size because either it is enforced by the overflow checks // or `rhs` is zero when the overflow checks are disabled. // Indeed, in that case, rhs is replaced with 'predicate * rhs' + // + // Using the predicate as an offset is a small optimization: + // * if the predicate is true, then the offset is one and this assert that 'r> AcirContext { // quotient_var is the output of a brillig call self.bound_constraint_with_offset(quotient_var, q0_var, zero, max_q_bits, one)?; - // when q == q0, b*q+r can overflow so we need to bound r to avoid the overflow. + // when q == q0, q*b+r can overflow so we need to bound r to avoid the overflow. let size_predicate = self.eq_var(q0_var, quotient_var)?; let predicate = self.mul_var(size_predicate, predicate)?; // Ensure that there is no overflow, under q == q0 predicate @@ -957,11 +965,12 @@ impl> AcirContext { let max_r = F::from_be_bytes_reduce(&max_r_big.to_bytes_be()); let max_r_var = self.add_constant(max_r); - // Bound the remainder to be