Skip to content

Commit

Permalink
Implement UsortVerifyMultiplicityBody hint (#368)
Browse files Browse the repository at this point in the history
* Initial code 'skeleton' for UsortVerifyMultiplicityBody implementation

* Read positions from scope manager

* Cast positions interface into an array of uint64

* Add test case for empty scopes in ScopeManager

* Assign 'current_pos' value in scope

* Assign 'last_pos' value in scope

* Assign 'next_item_index' value in address/memory

* Remove custom message from errors

* Make 'Pop' helper function return error if len of slice is 0

* Remove unnecessary tests

* Get value of 'last_pos' var from scope to calculate next_item_index value

* Change positions slice values to be in reverse order based on hint previous block

* Use uint functions instead of int since next_item_index value won't be negative

* Refactor tests

* Address code review comments

* Fix breaking test and get current_pos value from scope

* Return custom errors when values cannot be cast

* Add missing import after rebase

* Rename variables to use camelCase

* Cast values to int64
  • Loading branch information
wugalde19 committed May 6, 2024
1 parent 5305bb0 commit bc4b012
Show file tree
Hide file tree
Showing 5 changed files with 130 additions and 0 deletions.
1 change: 1 addition & 0 deletions pkg/hintrunner/zero/hintcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ const (
usortEnterScopeCode string = "vm_enter_scope(dict(__usort_max_size = globals().get('__usort_max_size')))"
usortVerifyMultiplicityAssertCode string = "assert len(positions) == 0"
usortVerifyCode string = "last_pos = 0\npositions = positions_dict[ids.value][::-1]"
usortVerifyMultiplicityBodyCode string = "current_pos = positions.pop()\nids.next_item_index = current_pos - last_pos\nlast_pos = current_pos + 1"

// ------ Elliptic Curve hints related code ------
ecNegateCode string = "from starkware.cairo.common.cairo_secp.secp_utils import SECP_P, pack\n\ny = pack(ids.point.y, PRIME) % SECP_P\n# The modulo operation in python always returns a nonnegative number.\nvalue = (-y) % SECP_P"
Expand Down
2 changes: 2 additions & 0 deletions pkg/hintrunner/zero/zerohint.go
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,8 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64
return createUsortVerifyMultiplicityAssertHinter()
case usortVerifyCode:
return createUsortVerifyHinter(resolver)
case usortVerifyMultiplicityBodyCode:
return createUsortVerifyMultiplicityBodyHinter(resolver)
// Dictionaries hints
case squashDictInnerAssertLenKeys:
return createSquashDictInnerAssertLenKeysHinter()
Expand Down
77 changes: 77 additions & 0 deletions pkg/hintrunner/zero/zerohint_usort.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"github.com/NethermindEth/cairo-vm-go/pkg/hintrunner/hinter"
"github.com/NethermindEth/cairo-vm-go/pkg/utils"
VM "github.com/NethermindEth/cairo-vm-go/pkg/vm"
"github.com/NethermindEth/cairo-vm-go/pkg/vm/memory"
"github.com/consensys/gnark-crypto/ecc/stark-curve/fp"
)

Expand Down Expand Up @@ -107,3 +108,79 @@ func createUsortVerifyHinter(resolver hintReferenceResolver) (hinter.Hinter, err

return newUsortVerifyHinter(value), nil
}

func newUsortVerifyMultiplicityBodyHint(nextItemIndex hinter.ResOperander) hinter.Hinter {
return &GenericZeroHinter{
Name: "UsortVerifyMultiplicityBody",
Op: func(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error {
//> current_pos = positions.pop()
//> ids.next_item_index = current_pos - last_pos
//> last_pos = current_pos + 1

positionsInterface, err := ctx.ScopeManager.GetVariableValue("positions")
if err != nil {
return err
}

positions, ok := positionsInterface.([]int64)
if !ok {
return fmt.Errorf("cannot cast positionsInterface to []int64")
}

newCurrentPos, err := utils.Pop(&positions)
if err != nil {
return err
}

currentPos, err := ctx.ScopeManager.GetVariableValue("current_pos")
if err != nil {
return err
}

currentPosInt, ok := currentPos.(int64)
if !ok {
return fmt.Errorf("cannot cast current_pos to int64")
}

lastPos, err := ctx.ScopeManager.GetVariableValue("last_pos")
if err != nil {
return err
}

lastPosInt, ok := lastPos.(int64)
if !ok {
return fmt.Errorf("cannot cast last_pos to int64")
}

// Calculate `next_item_index` memory value
newNextItemIndexValue := currentPosInt - lastPosInt
newNextItemIndexMemoryValue := memory.MemoryValueFromInt(newNextItemIndexValue)

// Save `next_item_index` value in address
addrNextItemIndex, err := nextItemIndex.GetAddress(vm)
if err != nil {
return err
}

err = vm.Memory.WriteToAddress(&addrNextItemIndex, &newNextItemIndexMemoryValue)
if err != nil {
return err
}

// Save `current_pos` and `last_pos` values in scope variables
return ctx.ScopeManager.AssignVariables(map[string]any{
"current_pos": newCurrentPos,
"last_pos": int64(currentPosInt + 1),
})
},
}
}

func createUsortVerifyMultiplicityBodyHinter(resolver hintReferenceResolver) (hinter.Hinter, error) {
nextItemIndex, err := resolver.GetResOperander("next_item_index")
if err != nil {
return nil, err
}

return newUsortVerifyMultiplicityBodyHint(nextItemIndex), nil
}
35 changes: 35 additions & 0 deletions pkg/hintrunner/zero/zerohint_usort_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,5 +78,40 @@ func TestZeroHintUsort(t *testing.T) {
},
},
},
"UsortVerifyMultiplicityBody": {
// Tests when no variables (positions, last_pos) are in the scope.
{
makeHinter: func(ctx *hintTestContext) hinter.Hinter {
return newUsortVerifyMultiplicityBodyHint(ctx.operanders["next_item_index"])
},
errCheck: func(t *testing.T, ctx *hintTestContext, err error) {
require.NotNil(t, err)
},
},
// Tests when we can calculate new memory and variable values.
{
operanders: []*hintOperander{
{Name: "next_item_index", Kind: uninitialized},
},
ctxInit: func(ctx *hinter.HintRunnerContext) {
ctx.ScopeManager.EnterScope(map[string]any{
"positions": []int64{8, 6, 4},
"current_pos": int64(2),
"last_pos": int64(1),
})
},
makeHinter: func(ctx *hintTestContext) hinter.Hinter {
return newUsortVerifyMultiplicityBodyHint(ctx.operanders["next_item_index"])
},
check: func(t *testing.T, ctx *hintTestContext) {
allVarValueInScopeEquals(map[string]any{
"current_pos": int64(4),
"last_pos": int64(3),
})(t, ctx)

varValueEquals("next_item_index", feltInt64(1))(t, ctx)
},
},
},
})
}
15 changes: 15 additions & 0 deletions pkg/utils/arrays.go
Original file line number Diff line number Diff line change
@@ -1,7 +1,22 @@
package utils

import (
"fmt"
)

func Reverse[T any](a []T) {
for i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {
a[i], a[j] = a[j], a[i]
}
}

func Pop[T any](a *[]T) (T, error) {
if len(*a) == 0 {
var zeroValue T
return zeroValue, fmt.Errorf("cannot pop from an empty slice")
}

v := (*a)[len(*a)-1]
*a = (*a)[:len(*a)-1]
return v, nil
}

0 comments on commit bc4b012

Please sign in to comment.