-
Notifications
You must be signed in to change notification settings - Fork 14
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: add comparison operators #130
base: main
Are you sure you want to change the base?
Conversation
validate_in_range::<_, MOD_BITS>(result); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
moved the constraining of the output of __validate_gt_remainder
to a separate function called check_gt_with_flags
.
@@ -41,14 +41,19 @@ pub(crate) unconstrained fn __validate_in_field_compute_borrow_flags<let N: u32, | |||
pub(crate) unconstrained fn __validate_gt_remainder<let N: u32>( |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__validate_gt_remainder
now also returns the boolean value of the underflow.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the lhs
and rhs
are swapped if the underflow is true, and the rest of the logic stays the same
Changes to circuit sizes
🧾 Summary (10% most significant diffs)
Full diff report 👇
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for delving into this code! Just 3 minor comments :)
src/fns/constrained_ops.nr
Outdated
@@ -572,3 +557,41 @@ pub(crate) fn umod<let N: u32, let MOD_BITS: u32>( | |||
) -> [Field; N] { | |||
udiv_mod::<_, MOD_BITS>(params, numerator, divisor).1 | |||
} | |||
|
|||
// a comparison function. returns true if lhs > rhs and false otherwise | |||
pub(crate) fn cmp<let N: u32, let MOD_BITS: u32>(lhs: [Field; N], rhs: [Field; N]) -> bool { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure what the convention should be, but it looks like cmp
functions in the noir stdlib always return the Ordering
type. Perhaps, therefore, this function should be renamed to something like gt
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
if you check bignum.nr
I have an impl for the Cmp
trait that returns an ordering
object.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh, sorry, yes I saw that one. I just mean for this function in particular, perhaps its name should be changed to something that isn't cmp
.
} | ||
|
||
#[test] | ||
fn test_cmp_BN_2() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Perhaps it's also worth testing other common edge cases:
- 0 < 1
- 0 < -1
And if you're feeling adventurous: - Choose lots of random bignums
r
ands
check that ifr < s
thenr - (p-1)/2 > s
- The reason I suggest something like this is because these current tests only test limbs[0] being different; what if the other limbs aren't equal?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actually, I've written 0 < -1
... but I don't actually know what the convention should be for comparison.
I think in the Aztec codebase, we consider the "first half" of the field as "positive" numbers, and the 2nd half of the field as "negative" numbers. So we check whether things are < (p-1)/2.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For the U256
type, regular integer comparison makes sense.
For the prime field types, I'm less sure...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ah that's a good point, at the moment everything is being treated as usual integers between 0
and modulus
. I think it'll be quite ugly to have two different impl depending on whether a number is positive or negative.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, being consistent makes sense to me!
where | ||
Params: BigNumParamsGetter<N, MOD_BITS>, | ||
{ | ||
fn cmp(self, other: Self) -> Ordering { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we also implement this for the RuntimeBignum
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yep will do.
Co-authored-by: Michael Connor <[email protected]>
@iAmMichaelConnor this should be at a good place for a review! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nits but LGTM
@@ -921,3 +921,56 @@ fn test_sub_underflow_regression() { | |||
let c = b - a; | |||
assert(c.limbs == [0, 0, 0]); | |||
} | |||
|
|||
#[test] | |||
fn test_cmp_BN() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can you move these all inside of a submodule grouping all of the tests related to cmp
?
} | ||
|
||
#[test] | ||
fn test_cmp_BN_fuzz(seed: [u8; 2]) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn test_cmp_BN_fuzz(seed: [u8; 2]) { | |
fn test_cmp_BN_fuzz(seed: [u8; 5]) { |
There's currently only ~65k potential inputs here.
@@ -27,6 +27,8 @@ use crate::params::BigNumParams as P; | |||
* udiv_mod | |||
* udiv | |||
* umod | |||
* gt | |||
* check_gt_with_flags | |||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's just remove this comment. It's pretty meaningless.
let comp_result: bool = gt::<_, MOD_BITS>(self.limbs, other.limbs); | ||
if self.limbs == other.limbs { | ||
Ordering::equal() | ||
} else if comp_result { | ||
// there was an underflow | ||
Ordering::greater() | ||
} else { | ||
// there was no underflow | ||
Ordering::less() | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we push a cmp
function into constrained_ops
? We can then avoid the duplicated logic between here and RuntimeBigNum
.
Description
resolves #112
Problem*
Resolves
Summary*
Additional Context
PR Checklist*
cargo fmt
on default settings.