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

Add divrem hints #245

Merged
merged 22 commits into from
Sep 20, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
f1b2f6f
Add hint codes
entropidelic Sep 18, 2023
d60bf79
Move divrem hint codes to math hint codes
entropidelic Sep 18, 2023
3691ea7
Finish implementation of unsignedDivRem hint
entropidelic Sep 18, 2023
29938c7
Add some fields to range check builtin runner
entropidelic Sep 18, 2023
f769433
WIP implementation of signedDivRem hint
entropidelic Sep 18, 2023
6b54344
Dummy commit
entropidelic Sep 19, 2023
cbcffee
Merge remote-tracking branch 'origin/main' into add-divrem-hints
entropidelic Sep 19, 2023
91f1436
Save work in progress
entropidelic Sep 19, 2023
be0f705
Add FeltFromBigInt function
entropidelic Sep 19, 2023
c17e91e
Add unsigned div rem integration test
entropidelic Sep 19, 2023
967d072
Fix division bug and make integration test pass
entropidelic Sep 19, 2023
2b9a1c7
Merge remote-tracking branch 'origin/main' into add-divrem-hints
entropidelic Sep 19, 2023
8dcf49c
Save work in progress
entropidelic Sep 19, 2023
e568e66
Finished unit test for divrem hints
entropidelic Sep 19, 2023
ae96b35
Merge remote-tracking branch 'origin/main' into add-divrem-hints
entropidelic Sep 19, 2023
07619a8
Merge remote-tracking branch 'origin/main' into add-divrem-hints
entropidelic Sep 19, 2023
3683f83
Remove unused commented code
entropidelic Sep 19, 2023
849f17e
Merge branch 'main' into add-divrem-hints
entropidelic Sep 20, 2023
561a2a2
Merge remote-tracking branch 'origin/main' into add-divrem-hints
entropidelic Sep 20, 2023
590f61f
Remove nParts and bound from range check runner fields, add method to…
entropidelic Sep 20, 2023
43dfe76
Remove unused input parameter to NewRangeCheckBuiltinRunner
entropidelic Sep 20, 2023
6e56be7
Change number to constant in Bound impl
entropidelic Sep 20, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Save work in progress
  • Loading branch information
entropidelic committed Sep 19, 2023
commit 91f1436a506fd624933535d0a35cd6e8eab5cd80
50 changes: 50 additions & 0 deletions cairo_programs/signed_div_rem.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
%builtins output range_check
from starkware.cairo.common.math import signed_div_rem, assert_le
from starkware.cairo.common.serialize import serialize_word

func signed_div_rem_man{range_check_ptr}(value, div, bound) -> (q: felt, r: felt) {
let r = [range_check_ptr];
let biased_q = [range_check_ptr + 1]; // == q + bound.
let range_check_ptr = range_check_ptr + 2;
%{
from starkware.cairo.common.math_utils import as_int, assert_integer

assert_integer(ids.div)
assert 0 < ids.div <= PRIME // range_check_builtin.bound, \
f'div={hex(ids.div)} is out of the valid range.'

assert_integer(ids.bound)
assert ids.bound <= range_check_builtin.bound // 2, \
f'bound={hex(ids.bound)} is out of the valid range.'

int_value = as_int(ids.value, PRIME)
q, ids.r = divmod(int_value, ids.div)

assert -ids.bound <= q < ids.bound, \
f'{int_value} / {ids.div} = {q} is out of the range [{-ids.bound}, {ids.bound}).'

ids.biased_q = q + ids.bound
%}
let q = biased_q - bound;
assert value = q * div + r;
assert_le(r, div - 1);
assert_le(biased_q, 2 * bound - 1);
return (q, r);
}

func main{output_ptr: felt*, range_check_ptr: felt}() {
let (q_negative_expected, r_negative_expected) = signed_div_rem(-10, 3, 29);
let (q_negative, r_negative) = signed_div_rem_man(-10, 3, 29);
assert q_negative_expected = q_negative;
assert r_negative_expected = r_negative;
serialize_word(q_negative_expected);
serialize_word(q_negative);
serialize_word(r_negative_expected);
serialize_word(r_negative);

let (q_expected, r_expected) = signed_div_rem(10, 3, 29);
let (q, r) = signed_div_rem_man(10, 3, 29);
assert q_expected = q;
assert r_expected = r;
return ();
}
27 changes: 27 additions & 0 deletions cairo_programs/unsigned_div_rem.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
%builtins range_check
from starkware.cairo.common.math import unsigned_div_rem

func unsigned_div_rem_man{range_check_ptr}(value, div) -> (q: felt, r: felt) {
let r = [range_check_ptr];
let q = [range_check_ptr + 1];
let range_check_ptr = range_check_ptr + 2;
%{
from starkware.cairo.common.math_utils import assert_integer
assert_integer(ids.div)
assert 0 < ids.div <= PRIME // range_check_builtin.bound, \
f'div={hex(ids.div)} is out of the valid range.'
ids.q, ids.r = divmod(ids.value, ids.div)
%}

return (q, r);
}

func main{range_check_ptr: felt}() {
let (q, r) = unsigned_div_rem_man(10, 3);
let (expected_q, expected_r) = unsigned_div_rem(10, 3);
assert q = expected_q;
assert r = expected_r;
assert q = 3;
assert r = 1;
return ();
}
131 changes: 52 additions & 79 deletions pkg/hints/math_hints.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package hints

import (
"fmt"
"math/big"

"github.com/lambdaclass/cairo-vm.go/pkg/builtins"
Expand Down Expand Up @@ -68,38 +69,6 @@ func assert_not_zero(ids IdsManager, vm *VirtualMachine) error {
return nil
}

// Implements hint:from starkware.cairo.common.math.cairo
//
// %{
// from starkware.crypto.signature.signature import FIELD_PRIME
// from starkware.python.math_utils import div_mod, is_quad_residue, sqrt
//
// x = ids.x
// if is_quad_residue(x, FIELD_PRIME):
// ids.y = sqrt(x, FIELD_PRIME)
// else:
// ids.y = sqrt(div_mod(x, 3, FIELD_PRIME), FIELD_PRIME)
//
// %}
func is_quad_residue(ids IdsManager, vm *VirtualMachine) error {
x, err := ids.GetFelt("x", vm)
if err != nil {
return err
}
if x.IsZero() || x.IsOne() {
ids.Insert("y", NewMaybeRelocatableFelt(x), vm)

} else if x.Pow(SignedFeltMaxValue()) == FeltOne() {
num := x.Sqrt()
ids.Insert("y", NewMaybeRelocatableFelt(num), vm)

} else {
num := (x.Div(lambdaworks.FeltFromUint64(3))).Sqrt()
ids.Insert("y", NewMaybeRelocatableFelt(num), vm)
}
return nil
}

func assert_not_equal(ids IdsManager, vm *VirtualMachine) error {
// Extract Ids Variables
a, err := ids.Get("a", vm)
Expand Down Expand Up @@ -180,12 +149,15 @@ func unsignedDivRem(ids IdsManager, vm *VirtualMachine) error {
return err
}

limit := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt())
if rcBound.Cmp(lambdaworks.FeltZero()) == 0 {
fmoletta marked this conversation as resolved.
Show resolved Hide resolved
return errors.New("range check bound cannot be zero")
}
primeBoundDivision := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt())

// Check if `div` is greater than `limit`
cmp := div.ToBigInt().Cmp(limit) == 1
divGreater := div.ToBigInt().Cmp(primeBoundDivision) == 1

if div.IsZero() || cmp {
if div.IsZero() || divGreater {
return errors.Errorf("Div out of range: 0 < %d <= %d", div, rcBound)
}

Expand Down Expand Up @@ -220,50 +192,51 @@ Implements hint:
%}
*/

// func signedDivRem(ids IdsManager, vm *VirtualMachine) error {
// div, err := ids.GetFelt("div", vm)
// if err != nil {
// return err
// }
// value, err := ids.GetFelt("value", vm)
// if err != nil {
// return err
// }
// bound, err := ids.GetFelt("bound", vm)
// if err != nil {
// return err
// }

// // It is safe to cast INNER_RC_BOUND into int64 since the constant is set to 65536
// rcBound, err := vm.GetRangeCheckBound()
// if err != nil {
// return err
// }

// if rcBound.Cmp(lambdaworks.FeltZero()) == 0 {
// return errors.New("range check bound cannot be zero")
// }
// primeBoundDivision := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt())

// // Check if `div` is greater than `limit` and make assertions
// divGreater := div.ToBigInt().Cmp(primeBoundDivision) == 1
// if div.IsZero() || divGreater {
// return errors.Errorf("div=%d is out of the valid range", div)
// }

// if bound.Cmp(rcBound.Shr(1)) == 1 {
// return errors.Errorf("bound=%d is out of the valid range")
// }

// sgnValue := value.ToSigned()
// sgnBound := bound.ToBigInt()
// intDiv := div.ToBigInt()

// q := new(big.Int).Div(sgnValue, intDiv)
// r := new(big.Int).Rem(sgnValue, intDiv)

// return nil
// }
func signedDivRem(ids IdsManager, vm *VirtualMachine) error {
div, err := ids.GetFelt("div", vm)
if err != nil {
return err
}
value, err := ids.GetFelt("value", vm)
if err != nil {
return err
}
bound, err := ids.GetFelt("bound", vm)
if err != nil {
return err
}

rcBound, err := vm.GetRangeCheckBound()
if err != nil {
return err
}

if rcBound.Cmp(lambdaworks.FeltZero()) == 0 {
fmoletta marked this conversation as resolved.
Show resolved Hide resolved
return errors.New("range check bound cannot be zero")
}
primeBoundDivision := new(big.Int).Div(lambdaworks.Prime(), rcBound.ToBigInt())

// Check if `div` is greater than `limit` and make assertions
divGreater := div.ToBigInt().Cmp(primeBoundDivision) == 1
if div.IsZero() || divGreater {
return errors.Errorf("div=%d is out of the valid range", div)
}

if bound.Cmp(rcBound.Shr(1)) == 1 {
return errors.Errorf("bound=%d is out of the valid range")
}

sgnValue := value.ToSigned()
sgnBound := bound.ToBigInt()
intDiv := div.ToBigInt()

q := new(big.Int).Div(sgnValue, intDiv)
r := new(big.Int).Rem(sgnValue, intDiv)

fmt.Println(sgnBound, q, r)

return nil
}

// pub fn signed_div_rem(
// vm: &mut VirtualMachine,
Expand Down
8 changes: 8 additions & 0 deletions pkg/vm/cairo_run/cairo_run_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -202,3 +202,11 @@ func TestSqrtHint(t *testing.T) {
t.Errorf("Program execution failed with error: %s", err)
}
}

func TestUnsignedDivRemHint(t *testing.T) {
cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false}
_, err := cairo_run.CairoRun("../../../cairo_programs/sqrt.json", cairoRunConfig)
if err != nil {
t.Errorf("Program execution failed with error: %s", err)
}
}