Skip to content

Commit

Permalink
Add set_add hint (#241)
Browse files Browse the repository at this point in the history
* Add set_add hint

* Add error to get range

* Revert memcpy changes

* Revert more changes

* Add basic find element hint impl

* Add search sorted lower and some tests

* Run format

* Add set_hints_test

* Add error to GetRange

* Move hint codes

* Make cell presence check more thorough

* Use camel case

* Use get in memory GetRange

---------

Co-authored-by: juan.mv <[email protected]>
  • Loading branch information
Juan-M-V and juan.mv authored Sep 22, 2023
1 parent bea82f0 commit 6c1fd10
Show file tree
Hide file tree
Showing 17 changed files with 856 additions and 11 deletions.
27 changes: 27 additions & 0 deletions cairo_programs/find_element.cairo
Original file line number Diff line number Diff line change
@@ -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 ();
}
23 changes: 23 additions & 0 deletions cairo_programs/search_sorted_lower.cairo
Original file line number Diff line number Diff line change
@@ -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 ();
}
30 changes: 30 additions & 0 deletions cairo_programs/set_add.cairo
Original file line number Diff line number Diff line change
@@ -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 ();
}
151 changes: 151 additions & 0 deletions pkg/hints/find_element_hints.go
Original file line number Diff line number Diff line change
@@ -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)
}
Loading

0 comments on commit 6c1fd10

Please sign in to comment.