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

Implement math_cmp hints #232

Merged
merged 12 commits into from
Sep 19, 2023
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