Skip to content

Commit

Permalink
Implement math_cmp hints (#232)
Browse files Browse the repository at this point in the history
* Add hint codes

* Add IS_NN hint + tests

* Add IS_NN_OUT_OF_RANGE hint

* Add tests

* Add IS_LE_FELT hint + tests

* Add integration test

* Move consts, use named import

* Fix fmt

---------

Co-authored-by: Juan-M-V <[email protected]>
  • Loading branch information
fmoletta and Juan-M-V authored Sep 19, 2023
1 parent 9c2a7f0 commit cb0c401
Show file tree
Hide file tree
Showing 7 changed files with 339 additions and 2 deletions.
63 changes: 63 additions & 0 deletions cairo_programs/math_cmp.cairo
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
%builtins range_check

from starkware.cairo.common.math_cmp import (
is_not_zero,
is_nn,
is_le,
is_nn_le,
is_in_range,
is_le_felt,
)

func main{range_check_ptr: felt}() {
// is_not_zero
let a = is_not_zero(10);
assert a = 1;
let b = is_not_zero(1);
assert b = 1;
let c = is_not_zero(0);
assert c = 0;

// is_nn
let d = is_nn(0);
assert d = 1;
let e = is_nn(88);
assert e = 1;
let f = is_nn(-88);
assert f = 0;

// is_le
let g = is_le(1, 2);
assert g = 1;
let h = is_le(2, 2);
assert h = 1;
let i = is_le(56, 20);
assert i = 0;

// is_nn_le
let j = is_nn_le(1, 2);
assert j = 1;
let k = is_nn_le(2, 2);
assert k = 1;
let l = is_nn_le(56, 20);
assert l = 0;

// is_in_range
let m = is_in_range(1, 2, 3);
assert m = 0;
let n = is_in_range(2, 2, 5);
assert n = 1;
let o = is_in_range(56, 20, 120);
assert o = 1;

// TODO: Uncomment once ASSERT_LE_FELT hint is implemented
// is_le_felt
// let p = is_le_felt(1, 2);
// assert p = 1;
// let q = is_le_felt(2, 2);
// assert q = 1;
// let r = is_le_felt(56, 20);
// assert r = 0;

return ();
}
7 changes: 7 additions & 0 deletions pkg/hints/hint_codes/math_cmp_hint_codes.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package hint_codes

const IS_NN = "memory[ap] = 0 if 0 <= (ids.a % PRIME) < range_check_builtin.bound else 1"

const IS_NN_OUT_OF_RANGE = "memory[ap] = 0 if 0 <= ((-ids.a - 1) % PRIME) < range_check_builtin.bound else 1"

const IS_LE_FELT = "memory[ap] = 0 if (ids.a % PRIME) <= (ids.b % PRIME) else 1"
6 changes: 6 additions & 0 deletions pkg/hints/hint_processor.go
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,12 @@ func (p *CairoVmHintProcessor) ExecuteHint(vm *vm.VirtualMachine, hintData *any,
return memcpy_enter_scope(data.Ids, vm, execScopes)
case VM_ENTER_SCOPE:
return vm_enter_scope(execScopes)
case IS_NN:
return isNN(data.Ids, vm)
case IS_NN_OUT_OF_RANGE:
return isNNOutOfRange(data.Ids, vm)
case IS_LE_FELT:
return isLeFelt(data.Ids, vm)
case ASSERT_250_BITS:
return Assert250Bit(data.Ids, vm, constants)
case SPLIT_FELT:
Expand Down
49 changes: 49 additions & 0 deletions pkg/hints/math_cmp_hints.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package hints

import (
"github.com/lambdaclass/cairo-vm.go/pkg/builtins"
. "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_utils"
. "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks"
. "github.com/lambdaclass/cairo-vm.go/pkg/vm"
. "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory"
)

// memory[ap] = 0 if 0 <= (ids.a % PRIME) < range_check_builtin.bound else 1
func isNN(ids IdsManager, vm *VirtualMachine) error {
a, err := ids.GetFelt("a", vm)
if err != nil {
return err
}
if a.Bits() < builtins.RANGE_CHECK_N_PARTS*builtins.INNER_RC_BOUND_SHIFT {
return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltZero()))
}
return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltOne()))
}

// memory[ap] = 0 if 0 <= ((-ids.a - 1) % PRIME) < range_check_builtin.bound else 1
func isNNOutOfRange(ids IdsManager, vm *VirtualMachine) error {
a, err := ids.GetFelt("a", vm)
if err != nil {
return err
}
if (FeltZero().Sub(a).Sub(FeltOne())).Bits() < builtins.RANGE_CHECK_N_PARTS*builtins.INNER_RC_BOUND_SHIFT {
return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltZero()))
}
return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltOne()))
}

// memory[ap] = 0 if (ids.a % PRIME) <= (ids.b % PRIME) else 1
func isLeFelt(ids IdsManager, vm *VirtualMachine) error {
a, err := ids.GetFelt("a", vm)
if err != nil {
return err
}
b, err := ids.GetFelt("b", vm)
if err != nil {
return err
}
if a.Cmp(b) != 1 {
return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltZero()))
}
return vm.Segments.Memory.Insert(vm.RunContext.Ap, NewMaybeRelocatableFelt(FeltOne()))
}
204 changes: 204 additions & 0 deletions pkg/hints/math_cmp_hints_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,204 @@
package hints_test

import (
"testing"

. "github.com/lambdaclass/cairo-vm.go/pkg/hints"
. "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_codes"
. "github.com/lambdaclass/cairo-vm.go/pkg/hints/hint_utils"
. "github.com/lambdaclass/cairo-vm.go/pkg/lambdaworks"
. "github.com/lambdaclass/cairo-vm.go/pkg/vm"
. "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory"
)

func TestIsNNHintZero(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
// Advance fp to avoid clashes with values inserted into ap
vm.RunContext.Fp.Offset += 1
idsManager := SetupIdsForTest(
map[string][]*MaybeRelocatable{
"a": {NewMaybeRelocatableFelt(FeltFromUint64(17))},
},
vm,
)
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Ids: idsManager,
Code: IS_NN,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil)
if err != nil {
t.Errorf("IS_NN hint test failed with error %s", err)
}
// Check the value of memory[ap]
val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap)
if err != nil || !val.IsZero() {
t.Error("Wrong/No value inserted into ap")
}
}

func TestIsNNHintOne(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
// Advance fp to avoid clashes with values inserted into ap
vm.RunContext.Fp.Offset += 1
idsManager := SetupIdsForTest(
map[string][]*MaybeRelocatable{
"a": {NewMaybeRelocatableFelt(FeltFromDecString("-1"))},
},
vm,
)
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Ids: idsManager,
Code: IS_NN,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil)
if err != nil {
t.Errorf("IS_NN hint test failed with error %s", err)
}
// Check the value of memory[ap]
val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap)
if err != nil || val != FeltOne() {
t.Error("Wrong/No value inserted into ap")
}
}

func TestIsNNOutOfRangeHintZero(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
// Advance fp to avoid clashes with values inserted into ap
vm.RunContext.Fp.Offset += 1
idsManager := SetupIdsForTest(
map[string][]*MaybeRelocatable{
"a": {NewMaybeRelocatableFelt(FeltFromDecString("-1"))},
},
vm,
)
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Ids: idsManager,
Code: IS_NN_OUT_OF_RANGE,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil)
if err != nil {
t.Errorf("IS_NN_OUT_OF_RANGE hint test failed with error %s", err)
}
// Check the value of memory[ap]
val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap)
if err != nil || !val.IsZero() {
t.Error("Wrong/No value inserted into ap")
}
}

func TestIsNNOutOfRangeHintOne(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
// Advance fp to avoid clashes with values inserted into ap
vm.RunContext.Fp.Offset += 1
idsManager := SetupIdsForTest(
map[string][]*MaybeRelocatable{
"a": {NewMaybeRelocatableFelt(FeltFromUint64(17))},
},
vm,
)
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Ids: idsManager,
Code: IS_NN_OUT_OF_RANGE,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil)
if err != nil {
t.Errorf("IS_NN_OUT_OF_RANGE hint test failed with error %s", err)
}
// Check the value of memory[ap]
val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap)
if err != nil || val != FeltOne() {
t.Error("Wrong/No value inserted into ap")
}
}

func TestIsLeFeltEq(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
// Advance fp to avoid clashes with values inserted into ap
vm.RunContext.Fp.Offset += 1
idsManager := SetupIdsForTest(
map[string][]*MaybeRelocatable{
"a": {NewMaybeRelocatableFelt(FeltFromUint64(17))},
"b": {NewMaybeRelocatableFelt(FeltFromUint64(17))},
},
vm,
)
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Ids: idsManager,
Code: IS_LE_FELT,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil)
if err != nil {
t.Errorf("IS_LE_FELT hint test failed with error %s", err)
}
// Check the value of memory[ap]
val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap)
if err != nil || !val.IsZero() {
t.Error("Wrong/No value inserted into ap")
}
}

func TestIsLeFeltLt(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
// Advance fp to avoid clashes with values inserted into ap
vm.RunContext.Fp.Offset += 1
idsManager := SetupIdsForTest(
map[string][]*MaybeRelocatable{
"a": {NewMaybeRelocatableFelt(FeltFromUint64(16))},
"b": {NewMaybeRelocatableFelt(FeltFromUint64(17))},
},
vm,
)
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Ids: idsManager,
Code: IS_LE_FELT,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil)
if err != nil {
t.Errorf("IS_LE_FELT hint test failed with error %s", err)
}
// Check the value of memory[ap]
val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap)
if err != nil || !val.IsZero() {
t.Error("Wrong/No value inserted into ap")
}
}

func TestIsLeFeltGt(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
// Advance fp to avoid clashes with values inserted into ap
vm.RunContext.Fp.Offset += 1
idsManager := SetupIdsForTest(
map[string][]*MaybeRelocatable{
"a": {NewMaybeRelocatableFelt(FeltFromUint64(18))},
"b": {NewMaybeRelocatableFelt(FeltFromUint64(17))},
},
vm,
)
hintProcessor := CairoVmHintProcessor{}
hintData := any(HintData{
Ids: idsManager,
Code: IS_LE_FELT,
})
err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil)
if err != nil {
t.Errorf("IS_LE_FELT hint test failed with error %s", err)
}
// Check the value of memory[ap]
val, err := vm.Segments.Memory.GetFelt(vm.RunContext.Ap)
if err != nil || val != FeltOne() {
t.Error("Wrong/No value inserted into ap")
}
}
4 changes: 2 additions & 2 deletions pkg/hints/math_hints_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import (
. "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory"
)

func TestIsNNHintOk(t *testing.T) {
func TestAssertNNHintOk(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
idsManager := SetupIdsForTest(
Expand All @@ -34,7 +34,7 @@ func TestIsNNHintOk(t *testing.T) {
}
}

func TestIsNNHintFail(t *testing.T) {
func TestAssertNNHintFail(t *testing.T) {
vm := NewVirtualMachine()
vm.Segments.AddSegment()
idsManager := SetupIdsForTest(
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 @@ -210,6 +210,14 @@ func TestSqrtHint(t *testing.T) {
}
}

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

func TestSquashDict(t *testing.T) {
cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "small", ProofMode: false}
_, err := cairo_run.CairoRun("../../../cairo_programs/squash_dict.json", cairoRunConfig)
Expand Down

0 comments on commit cb0c401

Please sign in to comment.