From 76c885557447814d7c302690ba361563112cce89 Mon Sep 17 00:00:00 2001 From: guipublic Date: Fri, 4 Aug 2023 16:56:39 +0000 Subject: [PATCH 1/5] avoid overflows in integer division --- .../brillig/brillig_gen/brillig_directive.rs | 50 +++++++++++++ .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 75 +++++++++++++++++-- .../ssa/acir_gen/acir_ir/generated_acir.rs | 2 + .../noirc_evaluator/src/ssa/acir_gen/mod.rs | 2 +- 4 files changed, 121 insertions(+), 8 deletions(-) diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index 93e760f9737..39d9741a790 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -87,3 +87,53 @@ pub(crate) fn directive_quotient(bit_size: u32) -> Vec { BrilligOpcode::Stop, ] } + +/// Generates brillig bytecode which computes `(a - pow) / b + 1` if a >= pow, +/// It returns `0` if a < pow +/// +/// +pub(crate) fn directive_truncate_helper(bit_size: u32) -> Vec { + // `a` is (0) (i.e register index 0) + // `b` is (1) + // `pow` is (2) + vec![ + // If the predicate is zero, we jump to the exit segment + BrilligOpcode::BinaryIntOp { + op: BinaryIntOp::LessThan, + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(2), + destination: RegisterIndex::from(3), + bit_size, + }, + BrilligOpcode::JumpIf { condition: RegisterIndex::from(3), location: 7 }, + //(0) = a-pow + BrilligOpcode::BinaryIntOp { + op: BinaryIntOp::Sub, + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(2), + destination: RegisterIndex::from(0), + bit_size, + }, + //(0) = (0)/(1) = (a-pow)/b + BrilligOpcode::BinaryIntOp { + op: BinaryIntOp::UnsignedDiv, + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + destination: RegisterIndex::from(0), + bit_size, + }, + BrilligOpcode::Const { destination: RegisterIndex::from(1), value: Value::from(1_usize) }, + //(0)= (0)+1 + BrilligOpcode::BinaryIntOp { + op: BinaryIntOp::Add, + lhs: RegisterIndex::from(0), + rhs: RegisterIndex::from(1), + destination: RegisterIndex::from(0), + bit_size, + }, + BrilligOpcode::Stop, + // Exit segment: we return 0 + BrilligOpcode::Const { destination: RegisterIndex::from(0), value: Value::from(0_usize) }, + BrilligOpcode::Stop, + ] +} diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 779aaa559ed..4515dfebdc4 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -20,6 +20,7 @@ use acvm::{ }; use iter_extended::{try_vecmap, vecmap}; use noirc_errors::Location; +use num_bigint::BigUint; use std::collections::HashMap; use std::{borrow::Cow, hash::Hash}; @@ -608,20 +609,80 @@ impl AcirContext { } /// Returns an `AcirVar` which will be constrained to be lhs mod 2^{rhs} - /// In order to do this, we simply perform euclidian division of lhs by 2^{rhs} + /// In order to do this, we 'simply' perform euclidian division of lhs by 2^{rhs} /// The remainder of the division is then lhs mod 2^{rhs} pub(crate) fn truncate_var( &mut self, - lhs: AcirVar, + mut lhs: AcirVar, rhs: u32, - max_bit_size: u32, + mut max_bit_size: u32, ) -> Result { - let lhs_data = &self.vars[&lhs]; - let lhs_expr = lhs_data.to_expression(); - // 2^{rhs} let divisor = FieldElement::from(2_i128).pow(&FieldElement::from(rhs as i128)); - // Computes lhs = 2^{rhs} * q + r + // Target bit size is the bit size of the field modulus with some margin. + // This margin is required for the euclidian division which does not work with field elements + let target_bit_size = FieldElement::max_num_bits() - 3; + + if max_bit_size > target_bit_size { + max_bit_size = target_bit_size; + // lhs is higher than the desired bit size, so we will remove multiples of the divisor from lhs + // until we get under the bit size. This will not change the end result. + let big_divisor = BigUint::from(2_u32).pow(rhs); + let one = self.add_constant(FieldElement::one()); + let divisor_var = self.add_constant(divisor); + // Create a variable quotient_high such that lhs-quotient_high*divisor is target_bit_size-bits + let quotient_high = self.add_variable(); + // Computes quotient_high non-deterministically + let target_bit_pow = + FieldElement::from(2_i128).pow(&FieldElement::from(target_bit_size as i128)); + let target_bit_pow_var = self.add_constant(target_bit_pow); + let truncate_code = + brillig_directive::directive_truncate_helper(FieldElement::max_num_bits()); + let field_type = AcirType::NumericType(NumericType::NativeField); + let inputs = vec![ + AcirValue::Var(lhs, field_type.clone()), + AcirValue::Var(divisor_var, field_type.clone()), + AcirValue::Var(target_bit_pow_var, field_type.clone()), + ]; + let quotient_high_value = self.brillig(one, truncate_code, inputs, vec![field_type])? + [0] + .clone() + .into_var()?; + self.assert_eq_var(quotient_high, quotient_high_value)?; + // Bounds quotient_high to q_max_big + // this bound is the smallest integer s.t p-1-bound*divisor < 2^target_bit_size + // since lhs is p-1 at max, we do not need an higher value + let q_max_big = (FieldElement::modulus() + - BigUint::from(1_u32) + - BigUint::from(2_u32).pow(target_bit_size)) + / big_divisor + + BigUint::from(1_u32); + let q_max_bits = q_max_big.bits() as u32; + let toto = FieldElement::modulus() + - BigUint::from(1_u32) + - BigUint::from(2_u32).pow(target_bit_size); + dbg!(toto.bits()); + let q_max_var = + self.add_constant(FieldElement::from_be_bytes_reduce(&q_max_big.to_bytes_be())); + self.range_constrain_var( + quotient_high, + &NumericType::Unsigned { bit_size: q_max_bits }, + )?; + dbg!(&q_max_big); + dbg!(&q_max_bits); + self.less_than_constrain(quotient_high, q_max_var, q_max_bits, one)?; + // Reduce lhs with quotient_high + let reduce = self.mul_var(quotient_high, divisor_var)?; + let lhs_reduce = self.sub_var(lhs, reduce)?; + let lhs_reduce_witness = self.var_to_witness(lhs_reduce)?; + // Constrain lhs_reduce to be reduced to the target_bit_size + self.acir_ir.range_constraint(lhs_reduce_witness, target_bit_size)?; + lhs = lhs_reduce; + } + + let lhs_data = &self.vars[&lhs]; + let lhs_expr = lhs_data.to_expression(); + // Computes lhs = 2^{rhs} * q + r let (_, remainder) = self.acir_ir.euclidean_division( &lhs_expr, &Expression::from_field(divisor), diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index b425eab42d3..dcbed2c5d99 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -432,6 +432,8 @@ impl GeneratedAcir { } } + // Avoids overflow: 'q*b+r < 2^max_q_bits*2^max_rhs_bits' + assert!(max_q_bits + max_rhs_bits < FieldElement::max_num_bits() - 1); let (q_witness, r_witness) = self.brillig_quotient(lhs.clone(), rhs.clone(), predicate.clone(), max_bit_size + 1); diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs index aca809a85fa..7409a199641 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/mod.rs @@ -1121,7 +1121,7 @@ impl Context { #[cfg(test)] mod tests { - use std::{rc::Rc, collections::HashMap}; + use std::{collections::HashMap, rc::Rc}; use acvm::{ acir::{ From a28ae0527dfe4cd7ea10f26b2be9308ffcab9e91 Mon Sep 17 00:00:00 2001 From: guipublic Date: Fri, 4 Aug 2023 17:12:30 +0000 Subject: [PATCH 2/5] fix failing tests --- crates/nargo_cli/tests/test_data/brillig_assert/src/main.nr | 2 +- .../nargo_cli/tests/test_data/brillig_conditional/src/main.nr | 2 +- crates/nargo_cli/tests/test_data/brillig_not/src/main.nr | 4 ++-- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/nargo_cli/tests/test_data/brillig_assert/src/main.nr b/crates/nargo_cli/tests/test_data/brillig_assert/src/main.nr index 320369c7b67..d77adac2ab0 100644 --- a/crates/nargo_cli/tests/test_data/brillig_assert/src/main.nr +++ b/crates/nargo_cli/tests/test_data/brillig_assert/src/main.nr @@ -2,7 +2,7 @@ // // The features being tested is using assert on brillig fn main(x: Field) { - assert(1 == conditional(x as bool)); + assert(1 == conditional(x == 1)); } unconstrained fn conditional(x : bool) -> Field { diff --git a/crates/nargo_cli/tests/test_data/brillig_conditional/src/main.nr b/crates/nargo_cli/tests/test_data/brillig_conditional/src/main.nr index 4ddd351ad04..1b0d124f7e3 100644 --- a/crates/nargo_cli/tests/test_data/brillig_conditional/src/main.nr +++ b/crates/nargo_cli/tests/test_data/brillig_conditional/src/main.nr @@ -2,7 +2,7 @@ // // The features being tested is basic conditonal on brillig fn main(x: Field) { - assert(4 == conditional(x as bool)); + assert(4 == conditional(x == 1)); } unconstrained fn conditional(x : bool) -> Field { diff --git a/crates/nargo_cli/tests/test_data/brillig_not/src/main.nr b/crates/nargo_cli/tests/test_data/brillig_not/src/main.nr index bc94810efb9..65872ff330c 100644 --- a/crates/nargo_cli/tests/test_data/brillig_not/src/main.nr +++ b/crates/nargo_cli/tests/test_data/brillig_not/src/main.nr @@ -2,8 +2,8 @@ // // The features being tested is not instruction on brillig fn main(x: Field, y : Field) { - assert(false == not_operator(x as bool)); - assert(true == not_operator(y as bool)); + assert(false == not_operator(x == 1)); + assert(true == not_operator(y == 1)); } unconstrained fn not_operator(x : bool) -> bool { From 6d4b2ce7bb961ba31f9347d3bca4c876d4518828 Mon Sep 17 00:00:00 2001 From: guipublic Date: Fri, 1 Sep 2023 12:31:58 +0000 Subject: [PATCH 3/5] code review: remove debug prints --- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 6 ------ 1 file changed, 6 deletions(-) diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 205bb82356a..4364391a32c 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -726,18 +726,12 @@ impl AcirContext { / big_divisor + BigUint::from(1_u32); let q_max_bits = q_max_big.bits() as u32; - let toto = FieldElement::modulus() - - BigUint::from(1_u32) - - BigUint::from(2_u32).pow(target_bit_size); - dbg!(toto.bits()); let q_max_var = self.add_constant(FieldElement::from_be_bytes_reduce(&q_max_big.to_bytes_be())); self.range_constrain_var( quotient_high, &NumericType::Unsigned { bit_size: q_max_bits }, )?; - dbg!(&q_max_big); - dbg!(&q_max_bits); self.less_than_constrain(quotient_high, q_max_var, q_max_bits, one)?; // Reduce lhs with quotient_high let reduce = self.mul_var(quotient_high, divisor_var)?; From b0e99efb13cd6c75edfbe79bef8d75d5fb820c42 Mon Sep 17 00:00:00 2001 From: guipublic Date: Fri, 1 Sep 2023 13:47:56 +0000 Subject: [PATCH 4/5] fix test by removing boolean cast --- .../tests/execution_success/brillig_assert/src/main.nr | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/nargo_cli/tests/execution_success/brillig_assert/src/main.nr b/crates/nargo_cli/tests/execution_success/brillig_assert/src/main.nr index 801a818c816..53619859dfa 100644 --- a/crates/nargo_cli/tests/execution_success/brillig_assert/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/brillig_assert/src/main.nr @@ -2,7 +2,7 @@ // // The features being tested is using assert on brillig fn main(x: Field) { - assert(1 == conditional(x as bool)); + assert(1 == conditional(x == 1)); } unconstrained fn conditional(x : bool) -> Field { From 832e811d137f599cdcd7f3c33bd052f0e977cd8e Mon Sep 17 00:00:00 2001 From: guipublic Date: Mon, 11 Sep 2023 09:49:35 +0000 Subject: [PATCH 5/5] better alternative for avoiding overflow in constant division --- .../arithmetic_binary_operations/src/main.nr | 6 +- .../brillig/brillig_gen/brillig_directive.rs | 50 -------------- .../src/ssa/acir_gen/acir_ir/acir_variable.rs | 65 +------------------ .../ssa/acir_gen/acir_ir/generated_acir.rs | 38 ++++++++++- 4 files changed, 44 insertions(+), 115 deletions(-) diff --git a/crates/nargo_cli/tests/execution_success/arithmetic_binary_operations/src/main.nr b/crates/nargo_cli/tests/execution_success/arithmetic_binary_operations/src/main.nr index 391aa27049d..201353393a6 100644 --- a/crates/nargo_cli/tests/execution_success/arithmetic_binary_operations/src/main.nr +++ b/crates/nargo_cli/tests/execution_success/arithmetic_binary_operations/src/main.nr @@ -1,9 +1,13 @@ // Tests a very simple program. // // The features being tested are: -// Binary addition, multiplication, division +// Binary addition, multiplication, division, constant modulo // x = 3, y = 4, z = 5 fn main(x : Field, y : Field, z : Field) -> pub Field { + //constant modulo + assert(x % 2 == 1); + assert(y as u1 == 0); + let a = x + x; // 3 + 3 = 6 let b = a - y; // 6 - 4 = 2 let c = b * z; // 2 * 5 = 10 diff --git a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs index bcafb7d2eda..ee7bbeed46e 100644 --- a/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs +++ b/crates/noirc_evaluator/src/brillig/brillig_gen/brillig_directive.rs @@ -106,53 +106,3 @@ pub(crate) fn directive_quotient(bit_size: u32) -> GeneratedBrillig { locations: Default::default(), } } - -/// Generates brillig bytecode which computes `(a - pow) / b + 1` if a >= pow, -/// It returns `0` if a < pow -/// -/// -pub(crate) fn directive_truncate_helper(bit_size: u32) -> Vec { - // `a` is (0) (i.e register index 0) - // `b` is (1) - // `pow` is (2) - vec![ - // If the predicate is zero, we jump to the exit segment - BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::LessThan, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(2), - destination: RegisterIndex::from(3), - bit_size, - }, - BrilligOpcode::JumpIf { condition: RegisterIndex::from(3), location: 7 }, - //(0) = a-pow - BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::Sub, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(2), - destination: RegisterIndex::from(0), - bit_size, - }, - //(0) = (0)/(1) = (a-pow)/b - BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::UnsignedDiv, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(0), - bit_size, - }, - BrilligOpcode::Const { destination: RegisterIndex::from(1), value: Value::from(1_usize) }, - //(0)= (0)+1 - BrilligOpcode::BinaryIntOp { - op: BinaryIntOp::Add, - lhs: RegisterIndex::from(0), - rhs: RegisterIndex::from(1), - destination: RegisterIndex::from(0), - bit_size, - }, - BrilligOpcode::Stop, - // Exit segment: we return 0 - BrilligOpcode::Const { destination: RegisterIndex::from(0), value: Value::from(0_usize) }, - BrilligOpcode::Stop, - ] -} diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs index 27bb246c4c7..374a78d7c86 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/acir_variable.rs @@ -22,8 +22,6 @@ use acvm::{ use acvm::{BlackBoxFunctionSolver, BlackBoxResolutionError}; use fxhash::FxHashMap as HashMap; use iter_extended::{try_vecmap, vecmap}; -use num_bigint::BigUint; -use std::collections::BTreeMap; use std::{borrow::Cow, hash::Hash}; #[derive(Clone, Debug, PartialEq, Eq, Hash)] @@ -692,71 +690,12 @@ impl AcirContext { /// The remainder of the division is then lhs mod 2^{rhs} pub(crate) fn truncate_var( &mut self, - mut lhs: AcirVar, + lhs: AcirVar, rhs: u32, - mut max_bit_size: u32, + max_bit_size: u32, ) -> Result { // 2^{rhs} let divisor = FieldElement::from(2_i128).pow(&FieldElement::from(rhs as i128)); - // Target bit size is the bit size of the field modulus with some margin. - // This margin is required for the euclidian division which does not work with field elements - let target_bit_size = FieldElement::max_num_bits() - 3; - - if max_bit_size > target_bit_size { - max_bit_size = target_bit_size; - // lhs is higher than the desired bit size, so we will remove multiples of the divisor from lhs - // until we get under the bit size. This will not change the end result. - let big_divisor = BigUint::from(2_u32).pow(rhs); - let one = self.add_constant(FieldElement::one()); - let divisor_var = self.add_constant(divisor); - // Create a variable quotient_high such that lhs-quotient_high*divisor is target_bit_size-bits - let quotient_high = self.add_variable(); - // Computes quotient_high non-deterministically - let target_bit_pow = - FieldElement::from(2_i128).pow(&FieldElement::from(target_bit_size as i128)); - let target_bit_pow_var = self.add_constant(target_bit_pow); - let truncate_code = - brillig_directive::directive_truncate_helper(FieldElement::max_num_bits()); - let field_type = AcirType::NumericType(NumericType::NativeField); - let inputs = vec![ - AcirValue::Var(lhs, field_type.clone()), - AcirValue::Var(divisor_var, field_type.clone()), - AcirValue::Var(target_bit_pow_var, field_type.clone()), - ]; - let truncate_brillig = GeneratedBrillig { - byte_code: truncate_code, - locations: BTreeMap::new(), - assert_messages: BTreeMap::new(), - }; - let quotient_high_value = - self.brillig(one, truncate_brillig, inputs, vec![field_type])?[0] - .clone() - .into_var()?; - self.assert_eq_var(quotient_high, quotient_high_value, None)?; - // Bounds quotient_high to q_max_big - // this bound is the smallest integer s.t p-1-bound*divisor < 2^target_bit_size - // since lhs is p-1 at max, we do not need an higher value - let q_max_big = (FieldElement::modulus() - - BigUint::from(1_u32) - - BigUint::from(2_u32).pow(target_bit_size)) - / big_divisor - + BigUint::from(1_u32); - let q_max_bits = q_max_big.bits() as u32; - let q_max_var = - self.add_constant(FieldElement::from_be_bytes_reduce(&q_max_big.to_bytes_be())); - self.range_constrain_var( - quotient_high, - &NumericType::Unsigned { bit_size: q_max_bits }, - )?; - self.less_than_constrain(quotient_high, q_max_var, q_max_bits, one)?; - // Reduce lhs with quotient_high - let reduce = self.mul_var(quotient_high, divisor_var)?; - let lhs_reduce = self.sub_var(lhs, reduce)?; - let lhs_reduce_witness = self.var_to_witness(lhs_reduce)?; - // Constrain lhs_reduce to be reduced to the target_bit_size - self.acir_ir.range_constraint(lhs_reduce_witness, target_bit_size)?; - lhs = lhs_reduce; - } let lhs_data = &self.vars[&lhs]; let lhs_expr = lhs_data.to_expression(); diff --git a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs index 9d03e53c9ca..3a43ec00811 100644 --- a/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs +++ b/crates/noirc_evaluator/src/ssa/acir_gen/acir_ir/generated_acir.rs @@ -456,7 +456,17 @@ impl GeneratedAcir { } // Avoids overflow: 'q*b+r < 2^max_q_bits*2^max_rhs_bits' - assert!(max_q_bits + max_rhs_bits < FieldElement::max_num_bits() - 1); + let mut avoid_overflow = false; + if max_q_bits + max_rhs_bits >= FieldElement::max_num_bits() - 1 { + // q*b+r can overflow; we avoid this when b is constant + if rhs.is_const() { + avoid_overflow = true; + } else { + // we do not support unbounded division + unreachable!("overflow in unbounded division"); + } + } + let (q_witness, r_witness) = self.brillig_quotient(lhs.clone(), rhs.clone(), predicate.clone(), max_bit_size + 1); @@ -483,6 +493,32 @@ impl GeneratedAcir { let div_euclidean = &self.mul_with_witness(lhs, predicate) - &self.mul_with_witness(&rhs_constraint, predicate); + if let Some(rhs_const) = rhs.to_const() { + if avoid_overflow { + // we compute q0 = p/rhs + let rhs_big = BigUint::from_bytes_be(&rhs_const.to_be_bytes()); + let q0_big = FieldElement::modulus() / &rhs_big; + let q0 = FieldElement::from_be_bytes_reduce(&q0_big.to_bytes_be()); + // when q == q0, b*q+r can overflow so we need to bound r to avoid the overflow. + let size_predicate = + self.is_equal(&Expression::from_field(q0), &Expression::from(q_witness)); + let predicate = self.mul_with_witness(&size_predicate.into(), predicate); + // Ensure that there is no overflow, under q == q0 predicate + let max_r_big = FieldElement::modulus() - q0_big * rhs_big; + let max_r = FieldElement::from_be_bytes_reduce(&max_r_big.to_bytes_be()); + let max_r_predicate = + self.mul_with_witness(&predicate, &Expression::from_field(max_r)); + let r_predicate = self.mul_with_witness(&Expression::from(r_witness), &predicate); + // Bound the remainder to be