Skip to content
This repository has been archived by the owner on Apr 9, 2024. It is now read-only.

Commit

Permalink
fix(brillig): Explicitly wrap on arithmetic operations (#365)
Browse files Browse the repository at this point in the history
Co-authored-by: ludamad <[email protected]>
  • Loading branch information
sirasistant and ludamad authored Jun 13, 2023
1 parent fda5dbe commit c0544a9
Showing 1 changed file with 94 additions and 11 deletions.
105 changes: 94 additions & 11 deletions brillig_vm/src/opcodes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,9 +164,9 @@ impl BinaryIntOp {
let bit_modulo = 1_u128 << bit_size;
match self {
// 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 => (a - b) % bit_modulo,
BinaryIntOp::Mul => (a * b) % bit_modulo,
BinaryIntOp::Add => a.wrapping_add(b) % bit_modulo,
BinaryIntOp::Sub => a.wrapping_sub(b) % bit_modulo,
BinaryIntOp::Mul => a.wrapping_mul(b) % bit_modulo,
// Perform unsigned division using the modulo operation on a and b.
BinaryIntOp::UnsignedDiv => (a % bit_modulo) / (b % bit_modulo),
// Perform signed division by first converting a and b to signed integers and then back to unsigned after the operation.
Expand Down Expand Up @@ -211,6 +211,91 @@ fn to_unsigned(a: i128, bit_size: u32) -> u128 {
mod tests {
use super::*;

struct TestParams {
a: u128,
b: u128,
result: u128,
}

fn to_negative(a: u128, bit_size: u32) -> u128 {
assert!(a > 0);
let two_pow = 2_u128.pow(bit_size);
two_pow - a
}

fn evaluate_int_ops(test_params: Vec<TestParams>, op: BinaryIntOp, bit_size: u32) {
for test in test_params {
assert_eq!(op.evaluate_int(test.a, test.b, bit_size), test.result);
}
}

#[test]
fn add_test() {
let bit_size = 4;

let test_ops = vec![
TestParams { a: 5, b: 10, result: 15 },
TestParams { a: 10, b: 10, result: 4 },
TestParams { a: 5, b: to_negative(3, bit_size), result: 2 },
TestParams { a: to_negative(3, bit_size), b: 1, result: to_negative(2, bit_size) },
TestParams { a: 5, b: to_negative(6, bit_size), result: to_negative(1, bit_size) },
];

evaluate_int_ops(test_ops, BinaryIntOp::Add, bit_size);
}

#[test]
fn sub_test() {
let bit_size = 4;

let test_ops = vec![
TestParams { a: 5, b: 3, result: 2 },
TestParams { a: 5, b: 10, result: to_negative(5, bit_size) },
TestParams { a: 5, b: to_negative(3, bit_size), result: 8 },
TestParams { a: to_negative(3, bit_size), b: 2, result: to_negative(5, bit_size) },
TestParams { a: 14, b: to_negative(3, bit_size), result: 1 },
];

evaluate_int_ops(test_ops, BinaryIntOp::Sub, bit_size);
}

#[test]
fn mul_test() {
let bit_size = 4;

let test_ops = vec![
TestParams { a: 5, b: 3, result: 15 },
TestParams { a: 5, b: 10, result: 2 },
TestParams { a: to_negative(1, bit_size), b: to_negative(5, bit_size), result: 5 },
TestParams { a: to_negative(1, bit_size), b: 5, result: to_negative(5, bit_size) },
TestParams {
a: to_negative(2, bit_size),
b: 7,
// negative 14 wraps to a 2
result: to_negative(14, bit_size),
},
];

evaluate_int_ops(test_ops, BinaryIntOp::Mul, bit_size);

let bit_size = 127;
let a = 2_u128.pow(bit_size) - 1;
let b = 3;

// ( 2**(n-1) - 1 ) * 3 = 2*2**(n-1) - 2 + (2**(n-1) - 1) => wraps to (2**(n-1) - 1) - 2
assert_eq!(BinaryIntOp::Mul.evaluate_int(a, b, bit_size), a - 2);
}

#[test]
fn div_test() {
let bit_size = 4;

let test_ops =
vec![TestParams { a: 5, b: 3, result: 1 }, TestParams { a: 5, b: 10, result: 0 }];

evaluate_int_ops(test_ops, BinaryIntOp::UnsignedDiv, bit_size);
}

#[test]
fn to_signed_roundtrip() {
let bit_size = 32;
Expand All @@ -221,15 +306,13 @@ mod tests {
#[test]
fn signed_div_test() {
let bit_size = 32;
let two_pow = 2_u128.pow(bit_size);

let minus_one = two_pow - 1;
let minus_five = two_pow - 5;
let minus_ten = two_pow - 10;
let test_ops = vec![
TestParams { a: 5, b: to_negative(10, bit_size), result: 0 },
TestParams { a: 5, b: to_negative(1, bit_size), result: to_negative(5, bit_size) },
TestParams { a: to_negative(5, bit_size), b: to_negative(1, bit_size), result: 5 },
];

let op = BinaryIntOp::SignedDiv;
assert_eq!(op.evaluate_int(5, minus_ten, bit_size), 0);
assert_eq!(op.evaluate_int(5, minus_one, bit_size), minus_five);
assert_eq!(op.evaluate_int(minus_five, minus_one, bit_size), 5);
evaluate_int_ops(test_ops, BinaryIntOp::SignedDiv, bit_size);
}
}

0 comments on commit c0544a9

Please sign in to comment.