Skip to content

Commit

Permalink
Cairo0 hint pow (#306)
Browse files Browse the repository at this point in the history
* Implement PowHint

* Add test cases

* Develop test cases, debug code based on test cases

* Make changes, descussed in the PR

* Refactored code, fixed memory bug

* Fixed the passing of CellRefers in hint generation

* Fixed bug with pow using uninitialized fields in memory in loop
  • Loading branch information
MaksymMalicki committed Mar 29, 2024
1 parent 183d8a9 commit aac12f3
Show file tree
Hide file tree
Showing 7 changed files with 111 additions and 10 deletions.
7 changes: 1 addition & 6 deletions pkg/hintrunner/hinter/operand.go
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,7 @@ func (v Immediate) ApplyApTracking(hint, ref zero.ApTracking) Reference {
return v
}

func GetConsecutiveValues(vm *VM.VirtualMachine, ref ResOperander, size int16) ([]mem.MemoryValue, error) {
addr, err := ref.GetAddress(vm)
if err != nil {
return nil, err
}

func GetConsecutiveValues(vm *VM.VirtualMachine, addr mem.MemoryAddress, size int16) ([]mem.MemoryValue, error) {
values := make([]mem.MemoryValue, size)
for i := int16(0); i < size; i++ {
nAddr, err := addr.AddOffset(i)
Expand Down
12 changes: 12 additions & 0 deletions pkg/hintrunner/zero/hint_reference_resolver.go
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,18 @@ func (m *hintReferenceResolver) GetResOperander(name string) (hinter.ResOperande
return op, nil
}

func (m *hintReferenceResolver) GetCellRefer(name string) (hinter.CellRefer, error) {
ref, err := m.GetReference(name)
if err != nil {
return nil, err
}
op, ok := ref.(hinter.CellRefer)
if !ok {
return nil, fmt.Errorf("expected %s to be CellRefer (got %T)", name, ref)
}
return op, nil
}

// shortSymbolName turns a full symbol name like "a.b.c" into just "c".
func shortSymbolName(name string) string {
i := strings.LastIndexByte(name, '.')
Expand Down
3 changes: 3 additions & 0 deletions pkg/hintrunner/zero/hintcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,9 @@ const (
splitIntAssertRange string = "assert ids.value == 0, 'split_int(): value is out of range.'"
splitIntCode string = "memory[ids.output] = res = (int(ids.value) % PRIME) % ids.base\nassert res < ids.bound, f'split_int(): Limb {res} is out of range.'"

// pow hints
powCode string = "ids.locs.bit = (ids.prev_locs.exp % PRIME) & 1"

unsignedDivRemCode string = "from starkware.cairo.common.math_utils import assert_integer\nassert_integer(ids.div)\nassert 0 < ids.div <= PRIME // range_check_builtin.bound, \\\n f'div={hex(ids.div)} is out of the valid range.'\nids.q, ids.r = divmod(ids.value, ids.div)"

// split_felt() hints.
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 @@ -82,6 +82,8 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64
return createSplitIntAssertRangeHinter(resolver)
case splitIntCode:
return createSplitIntHinter(resolver)
case powCode:
return createPowHinter(resolver)
case splitFeltCode:
return createSplitFeltHinter(resolver)
case sqrtCode:
Expand Down
55 changes: 53 additions & 2 deletions pkg/hintrunner/zero/zerohint_math.go
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,59 @@ func createSplitIntHinter(resolver hintReferenceResolver) (hinter.Hinter, error)
return newSplitIntHint(output, value, base, bound), nil
}

func newPowHint(locs, prevLocs hinter.ResOperander) hinter.Hinter {
return &GenericZeroHinter{
Name: "Pow",
Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error {
//> ids.locs.bit = (ids.prev_locs.exp % PRIME) & 1
/*> struct LoopLocals {
bit: felt,
temp0: felt,
res: felt,
base: felt,
exp: felt,
} */
const expStructOffset = 4
locsBitAddress, err := locs.GetAddress(vm)
if err != nil {
return err
}
prevLocsBitAddress, err := prevLocs.GetAddress(vm)
if err != nil {
return err
}
prevLocsExpAddr, err := vm.Memory.Read(prevLocsBitAddress.SegmentIndex, prevLocsBitAddress.Offset+expStructOffset)
if err != nil {
return err
}
prevLocsExp, err := prevLocsExpAddr.FieldElement()
if err != nil {
return err
}
var prevLocsExpBig big.Int
prevLocsExp.BigInt(&prevLocsExpBig)
locsBitBig := new(big.Int).And(&prevLocsExpBig, big.NewInt(1))
v := memory.MemoryValueFromFieldElement(new(fp.Element).SetBigInt(locsBitBig))
return vm.Memory.WriteToAddress(&locsBitAddress, &v)
},
}
}

func createPowHinter(resolver hintReferenceResolver) (hinter.Hinter, error) {
locs, err := resolver.GetCellRefer("locs")
if err != nil {
return nil, err
}
prev_locs, err := resolver.GetCellRefer("prev_locs")
if err != nil {
return nil, err
}
locsRes := hinter.Deref{Deref: locs}
prevLocsRes := hinter.Deref{Deref: prev_locs}
return newPowHint(locsRes, prevLocsRes), nil
}

func newSplitFeltHint(low, high, value hinter.ResOperander) hinter.Hinter {
return &GenericZeroHinter{
Name: "SplitFelt",
Expand Down Expand Up @@ -628,7 +681,6 @@ func newSqrtHint(root, value hinter.ResOperander) hinter.Hinter {
}

func createSqrtHinter(resolver hintReferenceResolver) (hinter.Hinter, error) {

root, err := resolver.GetResOperander("root")
if err != nil {
return nil, err
Expand Down Expand Up @@ -709,6 +761,5 @@ func createUnsignedDivRemHinter(resolver hintReferenceResolver) (hinter.Hinter,
if err != nil {
return nil, err
}

return newUnsignedDivRemHinter(value, div, q, r), nil
}
35 changes: 34 additions & 1 deletion pkg/hintrunner/zero/zerohint_math_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,40 @@ func TestZeroHintMath(t *testing.T) {
errCheck: errorTextContains("outside of the range [0, 2**250)"),
},
},

"Pow": {
{
operanders: []*hintOperander{
{Name: "prev_locs.bit", Kind: apRelative, Value: feltInt64(0)},
{Name: "prev_locs.temp0", Kind: apRelative, Value: feltInt64(0)},
{Name: "prev_locs.res", Kind: apRelative, Value: feltInt64(0)},
{Name: "prev_locs.base", Kind: apRelative, Value: feltInt64(0)},
{Name: "prev_locs.exp", Kind: apRelative, Value: feltInt64(256)},
{Name: "locs.bit", Kind: uninitialized},
},
makeHinter: func(ctx *hintTestContext) hinter.Hinter {
return newPowHint(ctx.operanders["locs.bit"], ctx.operanders["prev_locs.bit"])
},
check: allVarValueEquals(map[string]*fp.Element{
"locs.bit": feltInt64(0),
}),
},
{
operanders: []*hintOperander{
{Name: "prev_locs.bit", Kind: apRelative, Value: feltInt64(0)},
{Name: "prev_locs.temp0", Kind: apRelative, Value: feltInt64(0)},
{Name: "prev_locs.res", Kind: apRelative, Value: feltInt64(0)},
{Name: "prev_locs.base", Kind: apRelative, Value: feltInt64(0)},
{Name: "prev_locs.exp", Kind: apRelative, Value: feltInt64(255)},
{Name: "locs.bit", Kind: uninitialized},
},
makeHinter: func(ctx *hintTestContext) hinter.Hinter {
return newPowHint(ctx.operanders["locs.bit"], ctx.operanders["prev_locs.bit"])
},
check: allVarValueEquals(map[string]*fp.Element{
"locs.bit": feltInt64(1),
}),
},
},
"SplitFelt": {
{
operanders: []*hintOperander{
Expand Down
7 changes: 6 additions & 1 deletion pkg/hintrunner/zero/zerohint_uint256.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,12 @@ import (
)

func GetUint256AsFelts(vm *VM.VirtualMachine, ref hinter.ResOperander) (*fp.Element, *fp.Element, error) {
values, err := hinter.GetConsecutiveValues(vm, ref, int16(2))
refAddr, err := ref.GetAddress(vm)
if err != nil {
return nil, nil, err
}

values, err := hinter.GetConsecutiveValues(vm, refAddr, int16(2))
if err != nil {
return nil, nil, err
}
Expand Down

0 comments on commit aac12f3

Please sign in to comment.