diff --git a/cairo_programs/find_element.cairo b/cairo_programs/find_element.cairo new file mode 100644 index 00000000..f609f32a --- /dev/null +++ b/cairo_programs/find_element.cairo @@ -0,0 +1,27 @@ +%builtins range_check +from starkware.cairo.common.find_element import find_element +from starkware.cairo.common.alloc import alloc + +struct MyStruct { + a: felt, + b: felt, +} + +func main{range_check_ptr}() -> () { + // Create an array with MyStruct elements (1,2), (3,4), (5,6). + alloc_locals; + let (local array_ptr: MyStruct*) = alloc(); + assert array_ptr[0] = MyStruct(a=1, b=2); + assert array_ptr[1] = MyStruct(a=3, b=4); + assert array_ptr[2] = MyStruct(a=5, b=6); + + // Find any element with key '5'. + let (element_ptr: MyStruct*) = find_element( + array_ptr=array_ptr, elm_size=MyStruct.SIZE, n_elms=3, key=5 + ); + // A pointer to the element with index 2 is returned. + assert element_ptr.a = 5; + assert element_ptr.b = 6; + + return (); +} diff --git a/cairo_programs/search_sorted_lower.cairo b/cairo_programs/search_sorted_lower.cairo new file mode 100644 index 00000000..0ef26bef --- /dev/null +++ b/cairo_programs/search_sorted_lower.cairo @@ -0,0 +1,23 @@ +%builtins range_check +from starkware.cairo.common.find_element import search_sorted_lower +from starkware.cairo.common.alloc import alloc + +struct MyStruct { + a: felt, + b: felt, +} + +func main{range_check_ptr}() -> () { + // Create an array with MyStruct elements (1,2), (3,4), (5,6). + alloc_locals; + let (local array_ptr: MyStruct*) = alloc(); + assert array_ptr[0] = MyStruct(a=1, b=2); + assert array_ptr[1] = MyStruct(a=3, b=4); + assert array_ptr[2] = MyStruct(a=5, b=6); + let (smallest_ptr: MyStruct*) = search_sorted_lower( + array_ptr=array_ptr, elm_size=2, n_elms=3, key=2 + ); + assert smallest_ptr.a = 3; + assert smallest_ptr.b = 4; + return (); +} diff --git a/cairo_programs/set_add.cairo b/cairo_programs/set_add.cairo new file mode 100644 index 00000000..4113901d --- /dev/null +++ b/cairo_programs/set_add.cairo @@ -0,0 +1,30 @@ +%builtins range_check + +from starkware.cairo.common.alloc import alloc +from starkware.cairo.common.set import set_add + +struct MyStruct { + a: felt, + b: felt, +} + +func main{range_check_ptr}() { + alloc_locals; + + // An array containing two structs. + let (local my_list: MyStruct*) = alloc(); + assert my_list[0] = MyStruct(a=1, b=3); + assert my_list[1] = MyStruct(a=5, b=7); + + // Suppose that we want to add the element + // MyStruct(a=2, b=3) to my_list, but only if it is not already + // present (for the purpose of the example the contents of the + // array are known, but this doesn't have to be the case) + let list_end: felt* = &my_list[2]; + let (new_elm: MyStruct*) = alloc(); + assert new_elm[0] = MyStruct(a=2, b=3); + + set_add{set_end_ptr=list_end}(set_ptr=my_list, elm_size=MyStruct.SIZE, elm_ptr=new_elm); + assert my_list[2] = MyStruct(a=2, b=3); + return (); +} diff --git a/pkg/hints/find_element_hints.go b/pkg/hints/find_element_hints.go new file mode 100644 index 00000000..303003a1 --- /dev/null +++ b/pkg/hints/find_element_hints.go @@ -0,0 +1,151 @@ +package hints + +import ( + . "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/types" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" + "github.com/pkg/errors" +) + +func findElement(ids IdsManager, vm *VirtualMachine, execScopes ExecutionScopes) error { + arrayPtr, err := ids.GetRelocatable("array_ptr", vm) + if err != nil { + return err + } + + key, err := ids.GetFelt("key", vm) + if err != nil { + return err + } + + elmSizeFelt, err := ids.GetFelt("elm_size", vm) + if err != nil { + return err + } + elmSize, err := elmSizeFelt.ToUint() + if err != nil { + return err + } + + nElms, err := ids.GetFelt("n_elms", vm) + if err != nil { + return err + } + nElmsIter, err := nElms.ToUint() + if err != nil { + return err + } + + findElementIndexUncast, err := execScopes.Get("find_element_index") + if err == nil { + findElementIndex, ok := findElementIndexUncast.(Felt) + if !ok { + return ConversionError(findElementIndex, "felt") + } + position, err := arrayPtr.AddFelt(findElementIndex.Mul(elmSizeFelt)) + if err != nil { + return err + } + + foundKey, err := vm.Segments.Memory.GetFelt(position) + if err != nil { + return err + } + if foundKey != key { + return errors.Errorf( + "Invalid index found in find_element_index. Index: %s.\nExpected key: %s, found_key %s", + findElementIndex.ToSignedFeltString(), + key.ToSignedFeltString(), + foundKey.ToSignedFeltString(), + ) + } + execScopes.DeleteVariable("find_element_index") + return ids.Insert("index", NewMaybeRelocatableFelt(findElementIndex), vm) + } + + findElementMaxSizeUncast, err := execScopes.Get("find_element_max_size") + if err == nil { + findElementMaxSize, ok := findElementMaxSizeUncast.(Felt) + if !ok { + return ConversionError(findElementMaxSize, "felt") + } + if nElms.Cmp(findElementMaxSize) == 1 { + return errors.Errorf( + "find_element() can only be used with n_elms <= %s.\nGot: n_elms = %s", + findElementMaxSize.ToSignedFeltString(), + nElms.ToSignedFeltString(), + ) + } + } + + for i := uint(0); i < nElmsIter; i++ { + iterKey, err := vm.Segments.Memory.GetFelt(arrayPtr.AddUint(i * elmSize)) + if err != nil { + return err + } + if iterKey == key { + return ids.Insert("index", NewMaybeRelocatableFelt(FeltFromUint(i)), vm) + } + } + + return errors.Errorf("Key: %v was not found", key) +} + +func searchSortedLower(ids IdsManager, vm *VirtualMachine, execScopes ExecutionScopes) error { + arrayPtr, err := ids.GetRelocatable("array_ptr", vm) + if err != nil { + return err + } + + key, err := ids.GetFelt("key", vm) + if err != nil { + return err + } + + elmSizeFelt, err := ids.GetFelt("elm_size", vm) + if err != nil { + return err + } + elmSize, err := elmSizeFelt.ToUint() + if err != nil { + return err + } + + nElms, err := ids.GetFelt("n_elms", vm) + if err != nil { + return err + } + nElmsIter, err := nElms.ToUint() + if err != nil { + return err + } + + findElementMaxSizeUncast, err := execScopes.Get("find_element_max_size") + if err == nil { + findElementMaxSize, ok := findElementMaxSizeUncast.(Felt) + if !ok { + return ConversionError(findElementMaxSize, "felt") + } + if nElms.Cmp(findElementMaxSize) == 1 { + return errors.Errorf( + "find_element() can only be used with n_elms <= %s.\nGot: n_elms = %s", + findElementMaxSize.ToSignedFeltString(), + nElms.ToSignedFeltString(), + ) + } + } + + for i := uint(0); i < nElmsIter; i++ { + iterKey, err := vm.Segments.Memory.GetFelt(arrayPtr.AddUint(i * elmSize)) + if err != nil { + return err + } + if iterKey == key || iterKey.Cmp(key) == 1 { + return ids.Insert("index", NewMaybeRelocatableFelt(FeltFromUint(i)), vm) + } + } + + return errors.Errorf("Key: %v was not found", key) +} diff --git a/pkg/hints/find_element_hints_test.go b/pkg/hints/find_element_hints_test.go new file mode 100644 index 00000000..a9e74eda --- /dev/null +++ b/pkg/hints/find_element_hints_test.go @@ -0,0 +1,287 @@ +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/types" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm" + . "github.com/lambdaclass/cairo-vm.go/pkg/vm/memory" +) + +func TestFindElementHintOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.Memory.Insert(NewRelocatable(1, 0), NewMaybeRelocatableFelt(FeltFromUint64(1))) + vm.Segments.Memory.Insert(NewRelocatable(1, 1), NewMaybeRelocatableFelt(FeltFromUint64(2))) + vm.Segments.Memory.Insert(NewRelocatable(1, 2), NewMaybeRelocatableFelt(FeltFromUint64(3))) + vm.Segments.Memory.Insert(NewRelocatable(1, 3), NewMaybeRelocatableFelt(FeltFromUint64(4))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "array_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 0))}, + "elm_size": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "n_elms": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "key": {NewMaybeRelocatableFelt(FeltFromUint64(3))}, + "index": {nil}, + }, + vm, + ) + + execScopes := NewExecutionScopes() + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: FIND_ELEMENT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, execScopes) + if err != nil { + t.Errorf("FIND_ELEMENT hint test failed with error: %s", err) + } + index, err := idsManager.GetFelt("index", vm) + if err != nil { + t.Errorf("%s", err) + } + if index.Cmp(FeltFromUint64(1)) != 0 { + t.Errorf("Index was expected to be 1, got %s", index.ToSignedFeltString()) + } +} + +func TestFindElementWithFindElementIndex(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.Memory.Insert(NewRelocatable(1, 0), NewMaybeRelocatableFelt(FeltFromUint64(1))) + vm.Segments.Memory.Insert(NewRelocatable(1, 1), NewMaybeRelocatableFelt(FeltFromUint64(2))) + vm.Segments.Memory.Insert(NewRelocatable(1, 2), NewMaybeRelocatableFelt(FeltFromUint64(3))) + vm.Segments.Memory.Insert(NewRelocatable(1, 3), NewMaybeRelocatableFelt(FeltFromUint64(4))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "array_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 0))}, + "elm_size": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "n_elms": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "key": {NewMaybeRelocatableFelt(FeltFromUint64(3))}, + "index": {nil}, + }, + vm, + ) + + execScopes := NewExecutionScopes() + scope := make(map[string]interface{}) + scope["find_element_index"] = FeltOne() + execScopes.EnterScope(scope) + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: FIND_ELEMENT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, execScopes) + if err != nil { + t.Errorf("FIND_ELEMENT hint test failed with error: %s", err) + } + index, err := idsManager.GetFelt("index", vm) + if err != nil { + t.Errorf("%s", err) + } + if index.Cmp(FeltFromUint64(1)) != 0 { + t.Errorf("Index was expected to be 1, got %s", index.ToSignedFeltString()) + } +} + +func TestFindElementFindElementMaxSizeOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.Memory.Insert(NewRelocatable(1, 0), NewMaybeRelocatableFelt(FeltFromUint64(1))) + vm.Segments.Memory.Insert(NewRelocatable(1, 1), NewMaybeRelocatableFelt(FeltFromUint64(2))) + vm.Segments.Memory.Insert(NewRelocatable(1, 2), NewMaybeRelocatableFelt(FeltFromUint64(3))) + vm.Segments.Memory.Insert(NewRelocatable(1, 3), NewMaybeRelocatableFelt(FeltFromUint64(4))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "array_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 0))}, + "elm_size": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "n_elms": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "key": {NewMaybeRelocatableFelt(FeltFromUint64(3))}, + "index": {nil}, + }, + vm, + ) + + execScopes := NewExecutionScopes() + scope := make(map[string]interface{}) + scope["find_element_max_size"] = FeltFromUint64(2) + execScopes.EnterScope(scope) + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: FIND_ELEMENT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, execScopes) + if err != nil { + t.Errorf("FIND_ELEMENT hint test failed with error: %s", err) + } + index, err := idsManager.GetFelt("index", vm) + if err != nil { + t.Errorf("%s", err) + } + if index.Cmp(FeltFromUint64(1)) != 0 { + t.Errorf("Index was expected to be 1, got %s", index.ToSignedFeltString()) + } +} + +func TestFindElementFindElementMaxSizeLessThanNeeded(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.Memory.Insert(NewRelocatable(1, 0), NewMaybeRelocatableFelt(FeltFromUint64(1))) + vm.Segments.Memory.Insert(NewRelocatable(1, 1), NewMaybeRelocatableFelt(FeltFromUint64(2))) + vm.Segments.Memory.Insert(NewRelocatable(1, 2), NewMaybeRelocatableFelt(FeltFromUint64(3))) + vm.Segments.Memory.Insert(NewRelocatable(1, 3), NewMaybeRelocatableFelt(FeltFromUint64(4))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "array_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 0))}, + "elm_size": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "n_elms": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "key": {NewMaybeRelocatableFelt(FeltFromUint64(3))}, + "index": {nil}, + }, + vm, + ) + + execScopes := NewExecutionScopes() + scope := make(map[string]interface{}) + scope["find_element_max_size"] = FeltOne() + execScopes.EnterScope(scope) + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: FIND_ELEMENT, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, execScopes) + if err == nil { + t.Errorf("FIND_ELEMENT hint expected to fail with find_element_max_size < n_elms") + } +} + +func TestSearchSortedLowerHintOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.Memory.Insert(NewRelocatable(1, 0), NewMaybeRelocatableFelt(FeltFromUint64(1))) + vm.Segments.Memory.Insert(NewRelocatable(1, 1), NewMaybeRelocatableFelt(FeltFromUint64(2))) + vm.Segments.Memory.Insert(NewRelocatable(1, 2), NewMaybeRelocatableFelt(FeltFromUint64(3))) + vm.Segments.Memory.Insert(NewRelocatable(1, 3), NewMaybeRelocatableFelt(FeltFromUint64(4))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "array_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 0))}, + "elm_size": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "n_elms": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "key": {NewMaybeRelocatableFelt(FeltFromUint64(0))}, + "index": {nil}, + }, + vm, + ) + + execScopes := NewExecutionScopes() + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SEARCH_SORTED_LOWER, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, execScopes) + if err != nil { + t.Errorf("FIND_ELEMENT hint test failed with error: %s", err) + } + index, err := idsManager.GetFelt("index", vm) + if err != nil { + t.Errorf("%s", err) + } + if index.Cmp(FeltZero()) != 0 { + t.Errorf("Index was expected to be 0, got %s", index.ToSignedFeltString()) + } +} + +func TestSearchSortedLowerFindElementMaxSizeOk(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.Memory.Insert(NewRelocatable(1, 0), NewMaybeRelocatableFelt(FeltFromUint64(1))) + vm.Segments.Memory.Insert(NewRelocatable(1, 1), NewMaybeRelocatableFelt(FeltFromUint64(2))) + vm.Segments.Memory.Insert(NewRelocatable(1, 2), NewMaybeRelocatableFelt(FeltFromUint64(3))) + vm.Segments.Memory.Insert(NewRelocatable(1, 3), NewMaybeRelocatableFelt(FeltFromUint64(4))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "array_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 0))}, + "elm_size": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "n_elms": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "key": {NewMaybeRelocatableFelt(FeltFromUint64(3))}, + "index": {nil}, + }, + vm, + ) + + execScopes := NewExecutionScopes() + scope := make(map[string]interface{}) + scope["find_element_max_size"] = FeltFromUint64(2) + execScopes.EnterScope(scope) + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SEARCH_SORTED_LOWER, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, execScopes) + if err != nil { + t.Errorf("FIND_ELEMENT hint test failed with error: %s", err) + } + index, err := idsManager.GetFelt("index", vm) + if err != nil { + t.Errorf("%s", err) + } + if index.Cmp(FeltFromUint64(1)) != 0 { + t.Errorf("Index was expected to be 1, got %s", index.ToSignedFeltString()) + } +} + +func TestSearchSortedLowerFindElementMaxSizeLessThanNeeded(t *testing.T) { + vm := NewVirtualMachine() + vm.Segments.AddSegment() + vm.Segments.AddSegment() + vm.Segments.Memory.Insert(NewRelocatable(1, 0), NewMaybeRelocatableFelt(FeltFromUint64(1))) + vm.Segments.Memory.Insert(NewRelocatable(1, 1), NewMaybeRelocatableFelt(FeltFromUint64(2))) + vm.Segments.Memory.Insert(NewRelocatable(1, 2), NewMaybeRelocatableFelt(FeltFromUint64(3))) + vm.Segments.Memory.Insert(NewRelocatable(1, 3), NewMaybeRelocatableFelt(FeltFromUint64(4))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "array_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 0))}, + "elm_size": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "n_elms": {NewMaybeRelocatableFelt(FeltFromUint64(2))}, + "key": {NewMaybeRelocatableFelt(FeltFromUint64(3))}, + "index": {nil}, + }, + vm, + ) + + execScopes := NewExecutionScopes() + scope := make(map[string]interface{}) + scope["find_element_max_size"] = FeltOne() + execScopes.EnterScope(scope) + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SEARCH_SORTED_LOWER, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, execScopes) + if err == nil { + t.Errorf("FIND_ELEMENT hint expected to fail with find_element_max_size < n_elms") + } +} diff --git a/pkg/hints/hint_codes/find_element_hint_codes.go b/pkg/hints/hint_codes/find_element_hint_codes.go new file mode 100644 index 00000000..30bea4d2 --- /dev/null +++ b/pkg/hints/hint_codes/find_element_hint_codes.go @@ -0,0 +1,51 @@ +package hint_codes + +const FIND_ELEMENT = `array_ptr = ids.array_ptr +elm_size = ids.elm_size +assert isinstance(elm_size, int) and elm_size > 0, \ + f'Invalid value for elm_size. Got: {elm_size}.' +key = ids.key + +if '__find_element_index' in globals(): + ids.index = __find_element_index + found_key = memory[array_ptr + elm_size * __find_element_index] + assert found_key == key, \ + f'Invalid index found in __find_element_index. index: {__find_element_index}, ' \ + f'expected key {key}, found key: {found_key}.' + # Delete __find_element_index to make sure it's not used for the next calls. + del __find_element_index +else: + n_elms = ids.n_elms + assert isinstance(n_elms, int) and n_elms >= 0, \ + f'Invalid value for n_elms. Got: {n_elms}.' + if '__find_element_max_size' in globals(): + assert n_elms <= __find_element_max_size, \ + f'find_element() can only be used with n_elms<={__find_element_max_size}. ' \ + f'Got: n_elms={n_elms}.' + + for i in range(n_elms): + if memory[array_ptr + elm_size * i] == key: + ids.index = i + break + else: + raise ValueError(f'Key {key} was not found.')` + +const SEARCH_SORTED_LOWER = `array_ptr = ids.array_ptr +elm_size = ids.elm_size +assert isinstance(elm_size, int) and elm_size > 0, \ + f'Invalid value for elm_size. Got: {elm_size}.' + +n_elms = ids.n_elms +assert isinstance(n_elms, int) and n_elms >= 0, \ + f'Invalid value for n_elms. Got: {n_elms}.' +if '__find_element_max_size' in globals(): + assert n_elms <= __find_element_max_size, \ + f'find_element() can only be used with n_elms<={__find_element_max_size}. ' \ + f'Got: n_elms={n_elms}.' + +for i in range(n_elms): + if memory[array_ptr + elm_size * i] >= ids.key: + ids.index = i + break +else: + ids.index = n_elms` diff --git a/pkg/hints/hint_codes/set_hint_codes.go b/pkg/hints/hint_codes/set_hint_codes.go new file mode 100644 index 00000000..53354ac4 --- /dev/null +++ b/pkg/hints/hint_codes/set_hint_codes.go @@ -0,0 +1,3 @@ +package hint_codes + +const SET_ADD = "assert ids.elm_size > 0\nassert ids.set_ptr <= ids.set_end_ptr\nelm_list = memory.get_range(ids.elm_ptr, ids.elm_size)\nfor i in range(0, ids.set_end_ptr - ids.set_ptr, ids.elm_size):\n if memory.get_range(ids.set_ptr + i, ids.elm_size) == elm_list:\n ids.index = i // ids.elm_size\n ids.is_elm_in_set = 1\n break\nelse:\n ids.is_elm_in_set = 0" diff --git a/pkg/hints/hint_processor.go b/pkg/hints/hint_processor.go index 68950b4d..8718a624 100644 --- a/pkg/hints/hint_processor.go +++ b/pkg/hints/hint_processor.go @@ -106,6 +106,12 @@ 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 SET_ADD: + return setAdd(data.Ids, vm) + case FIND_ELEMENT: + return findElement(data.Ids, vm, *execScopes) + case SEARCH_SORTED_LOWER: + return searchSortedLower(data.Ids, vm, *execScopes) case COMPUTE_SLOPE_V1: return computeSlopeAndAssingSecpP(vm, *execScopes, data.Ids, "point0", "point1", SECP_P()) case EC_DOUBLE_SLOPE_V1: diff --git a/pkg/hints/set_hints.go b/pkg/hints/set_hints.go new file mode 100644 index 00000000..2435aa02 --- /dev/null +++ b/pkg/hints/set_hints.go @@ -0,0 +1,81 @@ +package hints + +import ( + . "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" + "github.com/pkg/errors" + "reflect" +) + +/* +Implements hint: + +assert ids.elm_size > 0 +assert ids.set_ptr <= ids.set_end_ptr +elm_list = memory.get_range(ids.elm_ptr, ids.elm_size) +for i in range(0, ids.set_end_ptr - ids.set_ptr, ids.elm_size): + + if memory.get_range(ids.set_ptr + i, ids.elm_size) == elm_list: + ids.index = i // ids.elm_size + ids.is_elm_in_set = 1 + break + +else: + + ids.is_elm_in_set = 0 +*/ +func setAdd(ids IdsManager, vm *VirtualMachine) error { + setPtr, err := ids.GetRelocatable("set_ptr", vm) + if err != nil { + return err + } + elmSizeFelt, err := ids.GetFelt("elm_size", vm) + if err != nil { + return err + } + elmPtr, err := ids.GetRelocatable("elm_ptr", vm) + if err != nil { + return err + } + setEndPtr, err := ids.GetRelocatable("set_end_ptr", vm) + if err != nil { + return err + } + + if elmSizeFelt.IsZero() { + return errors.Errorf("assert ids.elm_size > 0") + } + + elmSize, err := elmSizeFelt.ToUint() + + if err != nil { + return err + } + + if setPtr.Offset > setEndPtr.Offset { + return errors.Errorf("expected set_ptr: %v <= set_end_ptr: %v", setPtr, setEndPtr) + } + + elem, err := vm.Segments.Memory.GetRange(elmPtr, elmSize) + if err != nil { + return err + } + + for i := uint(0); i < setEndPtr.Offset-setPtr.Offset-elmSize; i++ { + otherElm, err := vm.Segments.Memory.GetRange(setPtr.AddUint(i*elmSize), elmSize) + if err != nil { + return err + } + if reflect.DeepEqual(elem, otherElm) { + err := ids.Insert("index", NewMaybeRelocatableFelt(FeltFromUint(i)), vm) + if err != nil { + return err + } + return ids.Insert("is_elm_in_set", NewMaybeRelocatableFelt(FeltOne()), vm) + } + } + + return ids.Insert("is_elm_in_set", NewMaybeRelocatableFelt(FeltZero()), vm) +} diff --git a/pkg/hints/set_hints_test.go b/pkg/hints/set_hints_test.go new file mode 100644 index 00000000..a7bbd9fa --- /dev/null +++ b/pkg/hints/set_hints_test.go @@ -0,0 +1,104 @@ +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 TestSetAddElmInSet(t *testing.T) { + vm := NewVirtualMachine() + // Initialize segments + vm.Segments.AddSegment() + vm.Segments.AddSegment() + // element to insert + vm.Segments.Memory.Insert(NewRelocatable(1, 0), NewMaybeRelocatableFelt(FeltFromUint64(2))) + // set + vm.Segments.Memory.Insert(NewRelocatable(1, 1), NewMaybeRelocatableFelt(FeltFromUint64(1))) + vm.Segments.Memory.Insert(NewRelocatable(1, 2), NewMaybeRelocatableFelt(FeltFromUint64(2))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "elm_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 0))}, + "set_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 1))}, + "set_end_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 4))}, + "elm_size": {NewMaybeRelocatableFelt(FeltFromUint64(1))}, + "index": {nil}, + "is_elm_in_set": {nil}, + }, + vm, + ) + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SET_ADD, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("SET_ADD failed with error: %s", err) + } + + isElmInSet, err := idsManager.GetFelt("is_elm_in_set", vm) + if err != nil { + t.Errorf("SET_ADD couldn't get is_elm_in_set: %s", err) + } + + if !isElmInSet.IsOne() { + t.Errorf("Expected is_elm_in_set to be 1, got: %s", isElmInSet.ToSignedFeltString()) + } + + index, err := idsManager.GetFelt("index", vm) + if err != nil { + t.Errorf("SET_ADD couldn't get index: %s", err) + } + if !index.IsOne() { + t.Errorf("Expected element to be found at 1, got index: %s", index.ToSignedFeltString()) + } +} + +func TestSetAddElmNotInSet(t *testing.T) { + vm := NewVirtualMachine() + // Initialize segments + vm.Segments.AddSegment() + vm.Segments.AddSegment() + // element to insert + vm.Segments.Memory.Insert(NewRelocatable(1, 0), NewMaybeRelocatableFelt(FeltFromUint64(3))) + // set + vm.Segments.Memory.Insert(NewRelocatable(1, 1), NewMaybeRelocatableFelt(FeltFromUint64(1))) + vm.Segments.Memory.Insert(NewRelocatable(1, 2), NewMaybeRelocatableFelt(FeltFromUint64(2))) + idsManager := SetupIdsForTest( + map[string][]*MaybeRelocatable{ + "elm_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 0))}, + "set_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 1))}, + "set_end_ptr": {NewMaybeRelocatableRelocatable(NewRelocatable(1, 3))}, + "elm_size": {NewMaybeRelocatableFelt(FeltFromUint64(1))}, + "index": {nil}, + "is_elm_in_set": {nil}, + }, + vm, + ) + + hintProcessor := CairoVmHintProcessor{} + hintData := any(HintData{ + Ids: idsManager, + Code: SET_ADD, + }) + err := hintProcessor.ExecuteHint(vm, &hintData, nil, nil) + if err != nil { + t.Errorf("SET_ADD failed with error: %s", err) + } + + isElmInSet, err := idsManager.GetFelt("is_elm_in_set", vm) + if err != nil { + t.Errorf("SET_ADD couldn't get is_elm_in_set: %s", err) + } + + if !isElmInSet.IsZero() { + t.Errorf("Expected is_elm_in_set to be 1, got: %s", isElmInSet.ToSignedFeltString()) + } +} diff --git a/pkg/lambdaworks/lambdaworks.go b/pkg/lambdaworks/lambdaworks.go index d6b27bdc..59f3a477 100644 --- a/pkg/lambdaworks/lambdaworks.go +++ b/pkg/lambdaworks/lambdaworks.go @@ -59,6 +59,12 @@ func FeltFromUint64(value uint64) Felt { return fromC(result) } +func FeltFromUint(value uint) Felt { + var result C.felt_t + C.from_uint(&result[0], C.uint_t(value)) + return fromC(result) +} + func FeltFromHex(value string) Felt { cs := C.CString(value) defer C.free(unsafe.Pointer(cs)) @@ -77,7 +83,7 @@ func FeltFromDecString(value string) Felt { return fromC(result) } -// turns a felt to usize +// turns a felt to u64 func (felt Felt) ToU64() (uint64, error) { if felt.limbs[0] == 0 && felt.limbs[1] == 0 && felt.limbs[2] == 0 { return uint64(felt.limbs[3]), nil @@ -86,6 +92,15 @@ func (felt Felt) ToU64() (uint64, error) { } } +// turns a felt to usize +func (felt Felt) ToUint() (uint, error) { + felt_u64, err := felt.ToU64() + if err != nil { + return 0, ConversionError(felt, "uint") + } + return uint(felt_u64), nil +} + func (felt Felt) ToLeBytes() *[32]byte { var result_c [32]C.uint8_t var value C.felt_t = felt.toC() diff --git a/pkg/lambdaworks/lambdaworks_test.go b/pkg/lambdaworks/lambdaworks_test.go index bffb7e7a..bf7c404d 100644 --- a/pkg/lambdaworks/lambdaworks_test.go +++ b/pkg/lambdaworks/lambdaworks_test.go @@ -426,6 +426,41 @@ func TestToU64Fail(t *testing.T) { t.Errorf("Conversion test should fail with error: %v", expected_err) } } + +func TestToUint1(t *testing.T) { + felt := lambdaworks.FeltOne() + result, err := felt.ToUint() + + var expected uint = 1 + + if expected != result { + t.Errorf("Error in conversion expected: %v, got %v with err: %v", expected, result, err) + } + +} + +func TestToUint10230(t *testing.T) { + felt := lambdaworks.FeltFromUint(10230) + result, err := felt.ToUint() + + var expected uint = 10230 + + if expected != result { + t.Errorf("Error in conversion expected: %v, got %v with err: %v", expected, result, err) + } +} + +func TestToUintFail(t *testing.T) { + felt := lambdaworks.FeltFromDecString("9999999999999999999999999") + + _, err := felt.ToUint() + expected_err := lambdaworks.ConversionError(felt, "uint") + + if err.Error() != expected_err.Error() { + t.Errorf("Conversion test should fail with error: %v", expected_err) + } +} + func TestFeltIsZero(t *testing.T) { f_zero := lambdaworks.FeltZero() diff --git a/pkg/lambdaworks/lib/lambdaworks.h b/pkg/lambdaworks/lib/lambdaworks.h index 850c9809..05764461 100644 --- a/pkg/lambdaworks/lib/lambdaworks.h +++ b/pkg/lambdaworks/lib/lambdaworks.h @@ -3,6 +3,7 @@ #include typedef uint64_t limb_t; +typedef unsigned int uint_t; /* A 256 bit prime field element (felt), represented as four limbs (integers). */ @@ -11,6 +12,9 @@ typedef limb_t felt_t[4]; /* Gets a felt_t representing the "value" number, in montgomery format. */ void from(felt_t result, uint64_t value); +/* Gets a felt_t representing the "value" number, in montgomery format. */ +void from_uint(felt_t result, uint_t value); + /*Gets a felt_t representing the "value" hexadecimal string, in montgomery * format. */ void from_hex(felt_t result, char *value); diff --git a/pkg/lambdaworks/lib/lambdaworks/src/lib.rs b/pkg/lambdaworks/lib/lambdaworks/src/lib.rs index b772d13d..14e27392 100644 --- a/pkg/lambdaworks/lib/lambdaworks/src/lib.rs +++ b/pkg/lambdaworks/lib/lambdaworks/src/lib.rs @@ -60,6 +60,11 @@ pub extern "C" fn from(result: Limbs, value: u64) { felt_to_limbs(Felt::from(value), result); } +#[no_mangle] +pub extern "C" fn from_uint(result: Limbs, value: usize) { + felt_to_limbs(Felt::from(value as u64), result); +} + #[no_mangle] pub extern "C" fn from_hex(result: Limbs, value: *const libc::c_char) { let val_cstr = unsafe { core::ffi::CStr::from_ptr(value) }; diff --git a/pkg/vm/cairo_run/cairo_run_test.go b/pkg/vm/cairo_run/cairo_run_test.go index aefaa7d0..33fc265f 100644 --- a/pkg/vm/cairo_run/cairo_run_test.go +++ b/pkg/vm/cairo_run/cairo_run_test.go @@ -147,6 +147,10 @@ func TestUnsignedDivRemHint(t *testing.T) { } } +func TestSetAddHint(t *testing.T) { + testProgram("set_add", t) +} + func TestMemcpyHint(t *testing.T) { testProgram("memcpy_test", t) } @@ -171,11 +175,15 @@ func TestSquashDict(t *testing.T) { } func TestSignedDivRemHint(t *testing.T) { - cairoRunConfig := cairo_run.CairoRunConfig{DisableTracePadding: false, Layout: "all_cairo", ProofMode: false} - _, err := cairo_run.CairoRun("../../../cairo_programs/signed_div_rem.json", cairoRunConfig) - if err != nil { - t.Errorf("Program execution failed with error: %s", err) - } + testProgram("signed_div_rem", t) +} + +func TestFindElementHint(t *testing.T) { + testProgram("find_element", t) +} + +func TestSearchSortedLowerHint(t *testing.T) { + testProgram("search_sorted_lower", t) } func TestAssert250BitHint(t *testing.T) { diff --git a/pkg/vm/memory/memory.go b/pkg/vm/memory/memory.go index 9985bc44..4939a8a0 100644 --- a/pkg/vm/memory/memory.go +++ b/pkg/vm/memory/memory.go @@ -150,6 +150,19 @@ func (m *Memory) GetFelt(addr Relocatable) (lambdaworks.Felt, error) { return lambdaworks.FeltZero(), err } +// Get a range of memory from the starting relocatable to the starting relocatable + size +func (m *Memory) GetRange(start Relocatable, size uint) ([]MaybeRelocatable, error) { + var res []MaybeRelocatable + for i := uint(0); i < size; i++ { + val, err := m.Get(start.AddUint(i)) + if err != nil { + return nil, err + } + res = append(res, *val) + } + return res, nil +} + // Adds a validation rule for a given segment func (m *Memory) AddValidationRule(SegmentIndex uint, rule ValidationRule) { m.validationRules[SegmentIndex] = rule diff --git a/pkg/vm/vm_core.go b/pkg/vm/vm_core.go index 1c3804de..7f5aec40 100644 --- a/pkg/vm/vm_core.go +++ b/pkg/vm/vm_core.go @@ -324,11 +324,13 @@ func (vm *VirtualMachine) DeduceOp1(instruction *Instruction, dst *memory.MaybeR return &dst_rel, dst, nil } case ResMul: - dst_felt, dst_is_felt := dst.GetFelt() - op0_felt, op0_is_felt := op0.GetFelt() - if dst_is_felt && op0_is_felt && !op0_felt.IsZero() { - res := memory.NewMaybeRelocatableFelt(dst_felt.Div(op0_felt)) - return res, dst, nil + if op0 != nil && dst != nil { + dst_felt, dst_is_felt := dst.GetFelt() + op0_felt, op0_is_felt := op0.GetFelt() + if dst_is_felt && op0_is_felt && !op0_felt.IsZero() { + res := memory.NewMaybeRelocatableFelt(dst_felt.Div(op0_felt)) + return res, dst, nil + } } } }