diff --git a/cairo_programs/math_cmp.cairo b/cairo_programs/math_cmp.cairo new file mode 100644 index 00000000..89dff06d --- /dev/null +++ b/cairo_programs/math_cmp.cairo @@ -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 (); +} diff --git a/pkg/hints/hint_codes/math_cmp_hint_codes.go b/pkg/hints/hint_codes/math_cmp_hint_codes.go new file mode 100644 index 00000000..fcd4f9f4 --- /dev/null +++ b/pkg/hints/hint_codes/math_cmp_hint_codes.go @@ -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" diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 7917d066..feda7fb3 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -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: diff --git a/pkg/hints/math_cmp_hints.go b/pkg/hints/math_cmp_hints.go new file mode 100644 index 00000000..5e4e064b --- /dev/null +++ b/pkg/hints/math_cmp_hints.go @@ -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())) +} diff --git a/pkg/hints/math_cmp_hints_test.go b/pkg/hints/math_cmp_hints_test.go new file mode 100644 index 00000000..6a0e47f2 --- /dev/null +++ b/pkg/hints/math_cmp_hints_test.go @@ -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") + } +} diff --git a/pkg/hints/math_hints_test.go b/pkg/hints/math_hints_test.go index 55198cc1..641a64f2 100644 --- a/pkg/hints/math_hints_test.go +++ b/pkg/hints/math_hints_test.go @@ -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( @@ -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( diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index 893a958f..325d75db 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -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)