Skip to content

Commit

Permalink
feat: signed arithmetic (#2748)
Browse files Browse the repository at this point in the history
  • Loading branch information
guipublic authored Sep 19, 2023
1 parent fb9ee4f commit a84216d
Show file tree
Hide file tree
Showing 6 changed files with 90 additions and 10 deletions.
6 changes: 1 addition & 5 deletions compiler/noirc_evaluator/src/ssa/acir_gen/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1059,18 +1059,14 @@ impl Context {
// Casting into a Field as a no-op
Ok(variable)
}
NumericType::Unsigned { bit_size } => {
if incoming_type.is_signed() {
todo!("Cast from unsigned to signed")
}
NumericType::Unsigned { bit_size } | NumericType::Signed { bit_size } => {
let max_bit_size = incoming_type.bit_size();
if max_bit_size <= *bit_size {
// Incoming variable already fits into target bit size - this is a no-op
return Ok(variable);
}
self.acir_context.truncate_var(variable, *bit_size, max_bit_size)
}
NumericType::Signed { .. } => todo!("Cast into signed"),
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
[package]
name = "signed_arithmetic"
type = "bin"
authors = [""]
compiler_version = "0.1"

[dependencies]
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
x = "5"
y = "8"
z = "-15"
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
fn main(mut x: i32, mut y: i32, z: i32) {
let mut s1: i8 = 5;
let mut s2: i8 = 8;
assert(s1+s2 == 13);
assert(x + y == 13);

s2= -8;
y = -y;
assert(s1+s2 == -3);
assert(x + y == -3);

s1 = -15;
assert(s1-s2 == -7);
assert(z-y == -7);

s1 = -5;
s2 = 8;
x = -x;
y = -y;
assert(s1-s2 == -13);
assert(x-y == -13);

s2 = -8;
y = -y;
assert(s1*s2 == 40);
assert(x*y == 40);

s1 = 1;
s2 = -8;
assert(s1*s2 == -8);
assert(x/x*y == -8);
}
42 changes: 40 additions & 2 deletions tooling/noirc_abi/src/input_parser/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use num_bigint::BigUint;
use num_traits::Num;
use num_bigint::{BigInt, BigUint};
use num_traits::{Num, Zero};
use std::collections::BTreeMap;

use acvm::FieldElement;
Expand Down Expand Up @@ -201,10 +201,48 @@ fn parse_str_to_field(value: &str) -> Result<FieldElement, InputParserError> {
}
}

fn parse_str_to_signed(value: &str, witdh: u32) -> Result<FieldElement, InputParserError> {
if value.starts_with("0x") {
FieldElement::from_hex(value).ok_or_else(|| InputParserError::ParseHexStr(value.to_owned()))
} else {
BigInt::from_str_radix(value, 10)
.map_err(|err_msg| InputParserError::ParseStr(err_msg.to_string()))
.and_then(|bigint| {
let modulus: BigInt = FieldElement::modulus().into();
let bigint = if bigint.sign() == num_bigint::Sign::Minus {
BigInt::from(2).pow(witdh) + bigint
} else {
bigint
};
if bigint.is_zero() || (bigint.sign() == num_bigint::Sign::Plus && bigint < modulus)
{
Ok(field_from_big_int(bigint))
} else {
Err(InputParserError::ParseStr(format!(
"Input exceeds field modulus. Values must fall within [0, {})",
FieldElement::modulus(),
)))
}
})
}
}

fn field_from_big_uint(bigint: BigUint) -> FieldElement {
FieldElement::from_be_bytes_reduce(&bigint.to_bytes_be())
}

fn field_from_big_int(bigint: BigInt) -> FieldElement {
match bigint.sign() {
num_bigint::Sign::Minus => {
unreachable!(
"Unsupported negative value; it should only be called with a positive value"
)
}
num_bigint::Sign::NoSign => FieldElement::zero(),
num_bigint::Sign::Plus => FieldElement::from_be_bytes_reduce(&bigint.to_bytes_be().1),
}
}

#[cfg(test)]
mod test {
use acvm::FieldElement;
Expand Down
10 changes: 7 additions & 3 deletions tooling/noirc_abi/src/input_parser/toml.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use super::{parse_str_to_field, InputValue};
use super::{parse_str_to_field, parse_str_to_signed, InputValue};
use crate::{errors::InputParserError, Abi, AbiType, MAIN_RETURN_NAME};
use acvm::FieldElement;
use iter_extended::{try_btree_map, try_vecmap};
Expand Down Expand Up @@ -118,9 +118,13 @@ impl InputValue {
(TomlTypes::String(string), AbiType::String { .. }) => InputValue::String(string),
(
TomlTypes::String(string),
AbiType::Field | AbiType::Integer { .. } | AbiType::Boolean,
AbiType::Field
| AbiType::Integer { sign: crate::Sign::Unsigned, .. }
| AbiType::Boolean,
) => InputValue::Field(parse_str_to_field(&string)?),

(TomlTypes::String(string), AbiType::Integer { sign: crate::Sign::Signed, width }) => {
InputValue::Field(parse_str_to_signed(&string, *width)?)
}
(
TomlTypes::Integer(integer),
AbiType::Field | AbiType::Integer { .. } | AbiType::Boolean,
Expand Down

0 comments on commit a84216d

Please sign in to comment.