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 19 commits
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
51 changes: 51 additions & 0 deletions cairo_programs/signed_div_rem.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
%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 ();
}
10 changes: 7 additions & 3 deletions pkg/builtins/range_check.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,18 @@ type RangeCheckBuiltinRunner struct {
ratio uint
instancesPerComponent uint
StopPtr *uint
nParts uint
Bound lambdaworks.Felt
fmoletta marked this conversation as resolved.
Show resolved Hide resolved
}

func NewRangeCheckBuiltinRunner(ratio uint) *RangeCheckBuiltinRunner {
return &RangeCheckBuiltinRunner{ratio: ratio, instancesPerComponent: 1}
func NewRangeCheckBuiltinRunner(ratio uint, nParts uint) *RangeCheckBuiltinRunner {
bound := lambdaworks.FeltOne().Shl(16 * uint64(nParts))

return &RangeCheckBuiltinRunner{ratio: ratio, instancesPerComponent: 1, nParts: nParts, Bound: bound}
}

func DefaultRangeCheckBuiltinRunner() *RangeCheckBuiltinRunner {
return NewRangeCheckBuiltinRunner(8)
return NewRangeCheckBuiltinRunner(8, 8)
}

func (r *RangeCheckBuiltinRunner) Base() memory.Relocatable {
Expand Down
8 changes: 4 additions & 4 deletions pkg/builtins/range_check_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ func TestGetAllocatedMemoryUnitsRangeCheck(t *testing.T) {
}

func TestGetRangeCheckUsageSuccessfulA(t *testing.T) {
var builtin = builtins.NewRangeCheckBuiltinRunner(8)
var builtin = builtins.DefaultRangeCheckBuiltinRunner()
builtin.Include(true)
builtin.SetBase(memory.NewRelocatable(0, 0))

Expand All @@ -82,7 +82,7 @@ func TestGetRangeCheckUsageSuccessfulA(t *testing.T) {
}

func TestGetRangeCheckUsageSuccessfulB(t *testing.T) {
var builtin = builtins.NewRangeCheckBuiltinRunner(8)
var builtin = builtins.DefaultRangeCheckBuiltinRunner()
builtin.Include(true)
builtin.SetBase(memory.NewRelocatable(0, 0))

Expand All @@ -106,7 +106,7 @@ func TestGetRangeCheckUsageSuccessfulB(t *testing.T) {
}

func TestGetRangeCheckUsageSuccessfulC(t *testing.T) {
var builtin = builtins.NewRangeCheckBuiltinRunner(8)
var builtin = builtins.DefaultRangeCheckBuiltinRunner()
builtin.Include(true)
builtin.SetBase(memory.NewRelocatable(0, 0))

Expand All @@ -132,7 +132,7 @@ func TestGetRangeCheckUsageSuccessfulC(t *testing.T) {
}

func TestGetRangeCheckUsageEmptyMemory(t *testing.T) {
var builtin = builtins.NewRangeCheckBuiltinRunner(8)
var builtin = builtins.DefaultRangeCheckBuiltinRunner()
builtin.Include(true)
builtin.SetBase(memory.NewRelocatable(0, 0))

Expand Down
4 changes: 4 additions & 0 deletions pkg/hints/hint_codes/math_hint_codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ const ASSERT_NOT_EQUAL = "from starkware.cairo.lang.vm.relocatable import Reloca

const SQRT = "from starkware.python.math_utils import isqrt\nvalue = ids.value % PRIME\nassert value < 2 ** 250, f\"value={value} is outside of the range [0, 2**250).\"\nassert 2 ** 250 < PRIME\nids.root = isqrt(value)"

const UNSIGNED_DIV_REM = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.div)\nassert 0 < ids.div <= PRIME // range_check_builtin.bound, \\\n f'div={hex(ids.div)} is out of the valid range.'\nids.q, ids.r = divmod(ids.value, ids.div)"

const SIGNED_DIV_REM = "from starkware.cairo.common.math_utils import as_int, assert_integer\n\nassert_integer(ids.div)\nassert 0 < ids.div <= PRIME // range_check_builtin.bound, \\\n f'div={hex(ids.div)} is out of the valid range.'\n\nassert_integer(ids.bound)\nassert ids.bound <= range_check_builtin.bound // 2, \\\n f'bound={hex(ids.bound)} is out of the valid range.'\n\nint_value = as_int(ids.value, PRIME)\nq, ids.r = divmod(int_value, ids.div)\n\nassert -ids.bound <= q < ids.bound, \\\n f'{int_value} / {ids.div} = {q} is out of the range [{-ids.bound}, {ids.bound}).'\n\nids.biased_q = q + ids.bound"

const ASSERT_LE_FELT = "import itertools\n\nfrom starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.a)\nassert_integer(ids.b)\na = ids.a % PRIME\nb = ids.b % PRIME\nassert a <= b, f'a = {a} is not less than or equal to b = {b}.'\n\n# Find an arc less than PRIME / 3, and another less than PRIME / 2.\nlengths_and_indices = [(a, 0), (b - a, 1), (PRIME - 1 - b, 2)]\nlengths_and_indices.sort()\nassert lengths_and_indices[0][0] <= PRIME // 3 and lengths_and_indices[1][0] <= PRIME // 2\nexcluded = lengths_and_indices[2][1]\n\nmemory[ids.range_check_ptr + 1], memory[ids.range_check_ptr + 0] = (\n divmod(lengths_and_indices[0][0], ids.PRIME_OVER_3_HIGH))\nmemory[ids.range_check_ptr + 3], memory[ids.range_check_ptr + 2] = (\n divmod(lengths_and_indices[1][0], ids.PRIME_OVER_2_HIGH))"

const ASSERT_LE_FELT_EXCLUDED_0 = "memory[ap] = 1 if excluded != 0 else 0"
Expand Down
4 changes: 4 additions & 0 deletions pkg/hints/hint_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,10 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any,
return memset_step_loop(data.Ids, vm, execScopes, "continue_loop")
case VM_ENTER_SCOPE:
return vm_enter_scope(execScopes)
case UNSIGNED_DIV_REM:
return unsignedDivRem(data.Ids, vm)
case SIGNED_DIV_REM:
return signedDivRem(data.Ids, vm)
case ASSERT_LE_FELT:
return assertLeFelt(data.Ids, vm, execScopes, constants)
case ASSERT_LE_FELT_EXCLUDED_0:
Expand Down
1 change: 1 addition & 0 deletions pkg/hints/hint_utils/testing_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ func SetupIdsForTest(ids map[string][]*memory.MaybeRelocatable, vm *VirtualMachi
}
// Update current_offset
current_offset += len(elems)

// Insert ids variables (if present)
for n, elem := range elems {
if elem != nil {
Expand Down
128 changes: 127 additions & 1 deletion pkg/hints/math_hints.go
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,54 @@ func sqrt(ids IdsManager, vm *VirtualMachine) error {
return nil
}

/*
Implements hint:

%{
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)
%}
*/
func unsignedDivRem(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
}

rcBound, err := vm.GetRangeCheckBound()
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`
divGreater := div.ToBigInt().Cmp(primeBoundDivision) == 1

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

q, r := value.DivRem(div)

err = ids.Insert("q", NewMaybeRelocatableFelt(q), vm)
if err != nil {
return err
}
err = ids.Insert("r", NewMaybeRelocatableFelt(r), vm)
if err != nil {
return err
}

return nil
}

func assertLeFelt(ids IdsManager, vm *VirtualMachine, scopes *ExecutionScopes, constants *map[string]Felt) error {
// Fetch constants
primeOver3HighFelt, err := ids.GetConst("PRIME_OVER_3_HIGH", constants)
Expand Down Expand Up @@ -327,7 +375,6 @@ func Assert250Bit(ids IdsManager, vm *VirtualMachine, constants *map[string]Felt
}

value, err := ids.GetFelt("value", vm)

if err != nil {
return err
}
Expand All @@ -351,6 +398,85 @@ func Assert250Bit(ids IdsManager, vm *VirtualMachine, constants *map[string]Felt
return nil
}

/*
Implements hint:

%{
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
%}
*/

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", bound)
}

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

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

if new(big.Int).Abs(intBound).Cmp(new(big.Int).Abs(q)) == -1 {
return errors.Errorf("%d / %d = %d is out of the range [-%d, %d]", sgnValue, div, q, bound, bound)
}

biasedQ := new(big.Int).Add(q, intBound)
biasedQFelt := lambdaworks.FeltFromBigInt(biasedQ)
rFelt := lambdaworks.FeltFromBigInt(r)

ids.Insert("r", NewMaybeRelocatableFelt(rFelt), vm)
ids.Insert("biased_q", NewMaybeRelocatableFelt(biasedQFelt), vm)

return nil
}

// Implements hint:
//
// %{
Expand Down
Loading