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

feat: implement euclidean division and signed division in terms of AcirVars #3230

Merged
merged 10 commits into from
Oct 24, 2023
24 changes: 18 additions & 6 deletions acvm-repo/brillig_vm/src/arithmetic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,18 +25,28 @@ pub(crate) fn evaluate_binary_bigint_op(
a: BigUint,
b: BigUint,
bit_size: u32,
) -> BigUint {
) -> Result<BigUint, String> {
let bit_modulo = &(BigUint::one() << bit_size);
match op {
let result = match op {
// Perform addition, subtraction, and multiplication, applying a modulo operation to keep the result within the bit size.
BinaryIntOp::Add => (a + b) % bit_modulo,
BinaryIntOp::Sub => (bit_modulo + a - b) % bit_modulo,
BinaryIntOp::Mul => (a * b) % bit_modulo,
// Perform unsigned division using the modulo operation on a and b.
BinaryIntOp::UnsignedDiv => (a % bit_modulo) / (b % bit_modulo),
BinaryIntOp::UnsignedDiv => {
let b_mod = b % bit_modulo;
if b_mod.is_zero() {
return Err("Division by zero".to_owned());
}
(a % bit_modulo) / b_mod
}
// Perform signed division by first converting a and b to signed integers and then back to unsigned after the operation.
BinaryIntOp::SignedDiv => {
let signed_div = to_big_signed(a, bit_size) / to_big_signed(b, bit_size);
let b_signed = to_big_signed(b, bit_size);
if b_signed.is_zero() {
return Err("Division by zero".to_owned());
}
let signed_div = to_big_signed(a, bit_size) / b_signed;
to_big_unsigned(signed_div, bit_size)
}
// Perform a == operation, returning 0 or 1
Expand Down Expand Up @@ -77,7 +87,9 @@ pub(crate) fn evaluate_binary_bigint_op(
let b = b.to_u128().unwrap();
(a >> b) % bit_modulo
}
}
};

Ok(result)
}

fn to_big_signed(a: BigUint, bit_size: u32) -> BigInt {
Expand Down Expand Up @@ -111,7 +123,7 @@ mod tests {
// Convert to big integers
let lhs_big = BigUint::from(a);
let rhs_big = BigUint::from(b);
let result_value = evaluate_binary_bigint_op(op, lhs_big, rhs_big, bit_size);
let result_value = evaluate_binary_bigint_op(op, lhs_big, rhs_big, bit_size).unwrap();
// Convert back to u128
result_value.to_u128().unwrap()
}
Expand Down
13 changes: 9 additions & 4 deletions acvm-repo/brillig_vm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -177,8 +177,12 @@
self.increment_program_counter()
}
Opcode::BinaryIntOp { op, bit_size, lhs, rhs, destination: result } => {
self.process_binary_int_op(*op, *bit_size, *lhs, *rhs, *result);
self.increment_program_counter()
if let Err(error) = self.process_binary_int_op(*op, *bit_size, *lhs, *rhs, *result)
{
self.fail(error)
} else {
self.increment_program_counter()
}
}
Opcode::Jump { location: destination } => self.set_program_counter(*destination),
Opcode::JumpIf { condition, location: destination } => {
Expand All @@ -201,7 +205,7 @@
if let Some(register) = self.call_stack.pop() {
self.set_program_counter(register.to_usize() + 1)
} else {
self.fail("return opcode hit, but callstack already empty".to_string())

Check warning on line 208 in acvm-repo/brillig_vm/src/lib.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (callstack)
}
}
Opcode::ForeignCall { function, destinations, inputs } => {
Expand Down Expand Up @@ -391,17 +395,18 @@
lhs: RegisterIndex,
rhs: RegisterIndex,
result: RegisterIndex,
) {
) -> Result<(), String> {
let lhs_value = self.registers.get(lhs);
let rhs_value = self.registers.get(rhs);

// Convert to big integers
let lhs_big = BigUint::from_bytes_be(&lhs_value.to_field().to_be_bytes());
let rhs_big = BigUint::from_bytes_be(&rhs_value.to_field().to_be_bytes());
let result_value = evaluate_binary_bigint_op(&op, lhs_big, rhs_big, bit_size);
let result_value = evaluate_binary_bigint_op(&op, lhs_big, rhs_big, bit_size)?;
// Convert back to field element
self.registers
.set(result, FieldElement::from_be_bytes_reduce(&result_value.to_bytes_be()).into());
Ok(())
}
}

Expand Down Expand Up @@ -516,7 +521,7 @@
}

#[test]
fn jmpifnot_opcode() {

Check warning on line 524 in acvm-repo/brillig_vm/src/lib.rs

View workflow job for this annotation

GitHub Actions / Spellcheck / Spellcheck

Unknown word (jmpifnot)
let input_registers =
Registers::load(vec![Value::from(1u128), Value::from(2u128), Value::from(0u128)]);

Expand Down
Loading
Loading