diff --git a/pkg/hintrunner/zero/zerohint.go b/pkg/hintrunner/zero/zerohint.go index caf7854ec..cfcaa52fe 100644 --- a/pkg/hintrunner/zero/zerohint.go +++ b/pkg/hintrunner/zero/zerohint.go @@ -67,11 +67,11 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64 case assertLeFeltCode: return createAssertLeFeltHinter(resolver) case assertLeFeltExcluded0Code: - return createAssertLeFeltExcluded0Hinter(resolver) + return createAssertLeFeltExcluded0Hinter() case assertLeFeltExcluded1Code: - return createAssertLeFeltExcluded1Hinter(resolver) + return createAssertLeFeltExcluded1Hinter() case assertLeFeltExcluded2Code: - return createAssertLeFeltExcluded2Hinter(resolver) + return createAssertLeFeltExcluded2Hinter() case isNNCode: return createIsNNHinter(resolver) case isNNOutOfRangeCode: diff --git a/pkg/hintrunner/zero/zerohint_math.go b/pkg/hintrunner/zero/zerohint_math.go index 7877a647e..e73a78123 100644 --- a/pkg/hintrunner/zero/zerohint_math.go +++ b/pkg/hintrunner/zero/zerohint_math.go @@ -15,17 +15,27 @@ import ( "github.com/consensys/gnark-crypto/ecc/stark-curve/fp" ) +// IsLeFelt hint determines if one value is less than or +// equal to another within a finite field +// +// `newIsLeFeltHint` takes 2 operanders as arguments +// - `a` and `b` are the values that will be compared +// +// `newIsLeFeltHint` writes to `[ap]` the result of the comparison +// i.e, 1 if `a % PRIME <= b % PRIME`, 0 otherwise func newIsLeFeltHint(a, b hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "IsLeFelt", Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { //> memory[ap] = 0 if (ids.a % PRIME) <= (ids.b % PRIME) else 1 + apAddr := vm.Context.AddressAp() a, err := hinter.ResolveAsFelt(vm, a) if err != nil { return err } + b, err := hinter.ResolveAsFelt(vm, b) if err != nil { return err @@ -37,6 +47,7 @@ func newIsLeFeltHint(a, b hinter.ResOperander) hinter.Hinter { } else { v = memory.MemoryValueFromFieldElement(&utils.FeltOne) } + return vm.Memory.WriteToAddress(&apAddr, &v) }, } @@ -47,13 +58,23 @@ func createIsLeFeltHinter(resolver hintReferenceResolver) (hinter.Hinter, error) if err != nil { return nil, err } + b, err := resolver.GetResOperander("b") if err != nil { return nil, err } + return newIsLeFeltHint(a, b), nil } +// AssertLtFelt hint asserts that one value is strictly +// less than another within a finite field +// +// `newAssertLtFeltHint` takes 2 operanders as arguments +// - `a` and `b` are the values that will be compared +// +// `newAssertLtFeltHint` returns an error if `a` is not +// strictly less than `b` within a finite field func newAssertLtFeltHint(a, b hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "AssertLtFelt", @@ -63,10 +84,12 @@ func newAssertLtFeltHint(a, b hinter.ResOperander) hinter.Hinter { //> assert_integer(ids.b) //> assert (ids.a % PRIME) < (ids.b % PRIME), //> f'a = {ids.a % PRIME} is not less than b = {ids.b % PRIME}.' + a, err := hinter.ResolveAsFelt(vm, a) if err != nil { return err } + b, err := hinter.ResolveAsFelt(vm, b) if err != nil { return err @@ -75,6 +98,7 @@ func newAssertLtFeltHint(a, b hinter.ResOperander) hinter.Hinter { if !utils.FeltLt(a, b) { return fmt.Errorf("a = %v is not less than b = %v", a, b) } + return nil }, } @@ -85,13 +109,21 @@ func createAssertLtFeltHinter(resolver hintReferenceResolver) (hinter.Hinter, er if err != nil { return nil, err } + b, err := resolver.GetResOperander("b") if err != nil { return nil, err } + return newAssertLtFeltHint(a, b), nil } +// AssertNotZero hint asserts that a value is not zero +// +// `newAssertNotZeroHint` takes 1 operander as argument +// - `value` is the value that will be compared to 0 +// +// `newAssertNotZeroHint` returns an error if `value` is zero func newAssertNotZeroHint(value hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "AssertNotZero", @@ -99,6 +131,7 @@ func newAssertNotZeroHint(value hinter.ResOperander) hinter.Hinter { //> from starkware.cairo.common.math_utils import assert_integer //> assert_integer(ids.value) //> assert ids.value % PRIME != 0, f'assert_not_zero failed: {ids.value} = 0.' + value, err := hinter.ResolveAsFelt(vm, value) if err != nil { return err @@ -107,6 +140,7 @@ func newAssertNotZeroHint(value hinter.ResOperander) hinter.Hinter { if value.IsZero() { return fmt.Errorf("assertion failed: value is zero") } + return nil }, } @@ -117,9 +151,18 @@ func createAssertNotZeroHinter(resolver hintReferenceResolver) (hinter.Hinter, e if err != nil { return nil, err } + return newAssertNotZeroHint(value), nil } +// AssertNN hint asserts that a given value is non-negative +// within the range `0 <= value % PRIME < range-check builtin bound` +// +// `newAssertNNHint` takes 1 operander as argument +// - `a` is the value that will be evaluated +// +// `newAssertNNHint` returns an error if `value` is negative +// within a specified range func newAssertNNHint(a hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "AssertNN", @@ -127,6 +170,7 @@ func newAssertNNHint(a hinter.ResOperander) hinter.Hinter { //> from starkware.cairo.common.math_utils import assert_integer //> assert_integer(ids.a) //> assert 0 <= ids.a % PRIME < range_check_builtin.bound, f'a = {ids.a} is out of range.' + a, err := hinter.ResolveAsFelt(vm, a) if err != nil { return err @@ -135,6 +179,7 @@ func newAssertNNHint(a hinter.ResOperander) hinter.Hinter { if !utils.FeltIsPositive(a) { return fmt.Errorf("assertion failed: a = %v is out of range", a) } + return nil }, } @@ -145,9 +190,16 @@ func createAssertNNHinter(resolver hintReferenceResolver) (hinter.Hinter, error) if err != nil { return nil, err } + return newAssertNNHint(a), nil } +// AssertNotEqual hint asserts that two given values are not equal +// +// `newAssertNotEqualHint` takes 2 operanders as arguments +// - `a` and `b` are the values that will be compared +// +// `newAssertNotEqualHint` returns an error if `a` and `b` are not equal func newAssertNotEqualHint(a, b hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "AssertNotEqual", @@ -165,6 +217,7 @@ func newAssertNotEqualHint(a, b hinter.ResOperander) hinter.Hinter { if err != nil { return err } + b, err := b.Resolve(vm) if err != nil { return err @@ -179,6 +232,7 @@ func newAssertNotEqualHint(a, b hinter.ResOperander) hinter.Hinter { if a.Equal(&b) { return fmt.Errorf("assertion failed: %v = %v", a, b) } + return nil }, } @@ -189,13 +243,24 @@ func createAssertNotEqualHinter(resolver hintReferenceResolver) (hinter.Hinter, if err != nil { return nil, err } + b, err := resolver.GetResOperander("b") if err != nil { return nil, err } + return newAssertNotEqualHint(a, b), nil } +// Assert250bits hint asserts that a value is within the range of 250 bits +// +// `newAssert250bitsHint` takes 3 operanders as arguments +// - `value` is the value that will be evaluated +// - `low` and `high` are the variables that will store the quotient and +// remainder of the modular division of `value` by 2**128 +// +// `newAssert250bitsHint` writes the quotient and the remainder of the modular +// division of `value` by 2**128 at `low` and `high` addresses in memory, respectively func newAssert250bitsHint(low, high, value hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "Assert250bits", @@ -214,6 +279,7 @@ func newAssert250bitsHint(low, high, value hinter.ResOperander) hinter.Hinter { if err != nil { return err } + if !utils.FeltLt(value, &utils.FeltUpperBound) { return fmt.Errorf("assertion failed: %v is outside of the range [0, 2**250)", value) } @@ -222,6 +288,7 @@ func newAssert250bitsHint(low, high, value hinter.ResOperander) hinter.Hinter { if err != nil { return err } + highAddr, err := high.GetAddress(vm) if err != nil { return err @@ -234,6 +301,7 @@ func newAssert250bitsHint(low, high, value hinter.ResOperander) hinter.Hinter { if err := vm.Memory.WriteToAddress(&highAddr, &divValue); err != nil { return err } + remValue := memory.MemoryValueFromFieldElement(&rem) if err := vm.Memory.WriteToAddress(&lowAddr, &remValue); err != nil { return err @@ -254,17 +322,26 @@ func createAssert250bitsHinter(resolver hintReferenceResolver) (hinter.Hinter, e if err != nil { return nil, err } + high, err := resolver.GetResOperander("high") if err != nil { return nil, err } + value, err := resolver.GetResOperander("value") if err != nil { return nil, err } + return newAssert250bitsHint(low, high, value), nil } +// AsserLeFelt hint assert that one value is less than or equal to another +// within a finite field +// +// `newAssertLeFeltHint` takes 3 operanders as arguments +// - `a` and `b` is the values that will be evaluated +// - `rangeCheckPtr` is a pointer to the range-check builtin func newAssertLeFeltHint(a, b, rangeCheckPtr hinter.ResOperander) hinter.Hinter { return &core.AssertLeFindSmallArc{ A: a, @@ -278,29 +355,36 @@ func createAssertLeFeltHinter(resolver hintReferenceResolver) (hinter.Hinter, er if err != nil { return nil, err } + b, err := resolver.GetResOperander("b") if err != nil { return nil, err } + rangeCheckPtr, err := resolver.GetResOperander("range_check_ptr") if err != nil { return nil, err } + return newAssertLeFeltHint(a, b, rangeCheckPtr), nil } -func createAssertLeFeltExcluded0Hinter(resolver hintReferenceResolver) (hinter.Hinter, error) { +// AssertLeFeltExcluded0 hint is a custom assertion related to `AssertLeFelt` hint +func createAssertLeFeltExcluded0Hinter() (hinter.Hinter, error) { return &core.AssertLeIsFirstArcExcluded{SkipExcludeAFlag: hinter.ApCellRef(0)}, nil } -func createAssertLeFeltExcluded1Hinter(resolver hintReferenceResolver) (hinter.Hinter, error) { +// AssertLeFeltExcluded1 hint is a custom assertion related to `AssertLeFelt` hint +func createAssertLeFeltExcluded1Hinter() (hinter.Hinter, error) { return &core.AssertLeIsSecondArcExcluded{SkipExcludeBMinusA: hinter.ApCellRef(0)}, nil } -func createAssertLeFeltExcluded2Hinter(resolver hintReferenceResolver) (hinter.Hinter, error) { - // This hint is Cairo0-specific. - // It only does a python-scoped variable named "excluded" assert. - // We store that variable inside a hinter context. +// AssertLeFeltExcluded2 hint is a custom assertion related to `AssertLeFelt` hint +func createAssertLeFeltExcluded2Hinter() (hinter.Hinter, error) { + // This hint is Cairo0-specific + // It only does a python-scoped variable named "excluded" assert + // We store that variable inside a hinter context + h := &GenericZeroHinter{ Name: "AssertLeFeltExcluded2", Op: func(vm *VM.VirtualMachine, ctx *hinter.HintRunnerContext) error { @@ -316,21 +400,32 @@ func createAssertLeFeltExcluded2Hinter(resolver hintReferenceResolver) (hinter.H return nil }, } + return h, nil } +// IsNN hint checks if a value is non-negative within the range +// `0 <= value % PRIME < range-check builtin bound` +// +// `newIsNNHint` takes 1 operander as argument +// - `a` is the value that will be evaluated +// +// `newIsNNHint` writes 0 or 1 to memory at `[ap]` address, depending on +// whether `a` is positive or negative, respectively func newIsNNHint(a hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "IsNN", Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { - apAddr := vm.Context.AddressAp() //> memory[ap] = 0 if 0 <= (ids.a % PRIME) < range_check_builtin.bound else 1 + apAddr := vm.Context.AddressAp() + // a is already modulo PRIME, no need to adjust it. a, err := hinter.ResolveAsFelt(vm, a) if err != nil { return err } + // range_check_builtin.bound is utils.FeltMax128 (1 << 128). var v memory.MemoryValue if utils.FeltIsPositive(a) { @@ -338,6 +433,7 @@ func newIsNNHint(a hinter.ResOperander) hinter.Hinter { } else { v = memory.MemoryValueFromFieldElement(&utils.FeltOne) } + return vm.Memory.WriteToAddress(&apAddr, &v) }, } @@ -348,19 +444,31 @@ func createIsNNHinter(resolver hintReferenceResolver) (hinter.Hinter, error) { if err != nil { return nil, err } + return newIsNNHint(a), nil } +// IsNNOutOfRange hint checks if the negation of a value minus one +// is non-negative within a specific range +// +// `newIsNNOutOfRangeHint` takes 1 operander as argument +// - `a` is the value that will be evaluated +// +// `newIsNNOutOfRangeHint` writes 0 or 1 to memory at `[ap]` address, depending on +// whether the negation of `a` minus one is positive or negative, respectively func newIsNNOutOfRangeHint(a hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "IsNNOutOfRange", Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { - apAddr := vm.Context.AddressAp() //> memory[ap] = 0 if 0 <= ((-ids.a - 1) % PRIME) < range_check_builtin.bound else 1 + + apAddr := vm.Context.AddressAp() + a, err := hinter.ResolveAsFelt(vm, a) if err != nil { return err } + var lhs fp.Element lhs.Sub(&utils.FeltZero, a) //> -ids.a lhs.Sub(&lhs, &utils.FeltOne) @@ -370,6 +478,7 @@ func newIsNNOutOfRangeHint(a hinter.ResOperander) hinter.Hinter { } else { v = memory.MemoryValueFromFieldElement(&utils.FeltOne) } + return vm.Memory.WriteToAddress(&apAddr, &v) }, } @@ -383,9 +492,19 @@ func createIsNNOutOfRangeHinter(resolver hintReferenceResolver) (hinter.Hinter, if err != nil { return nil, err } + return newIsNNOutOfRangeHint(a), nil } +// IsPositive hint checks if a value is positive within +// a specific range and prime context +// +// `newIsPositiveHint` takes 2 operanders as arguments +// - `value` is the value that will be evaluated +// - `dst` is the address where to write the result in memmory +// +// `newIsPositiveHint` writes 1 or 0 to `dest` address, depending on +// whether `value` is positive or negative in the context, respectively func newIsPositiveHint(value, dst hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "IsPositive", @@ -410,6 +529,7 @@ func newIsPositiveHint(value, dst hinter.ResOperander) hinter.Hinter { } else { v = memory.MemoryValueFromFieldElement(&utils.FeltZero) } + return vm.Memory.WriteToAddress(&isPositiveAddr, &v) }, } @@ -420,13 +540,22 @@ func createIsPositiveHinter(resolver hintReferenceResolver) (hinter.Hinter, erro if err != nil { return nil, err } + output, err := resolver.GetResOperander("output") if err != nil { return nil, err } + return newIsPositiveHint(value, output), nil } +// SplitIntAssertRange hint asserts that the value to split in `SplitInt` +// function is zero once there are no more limbs to create +// +// `newSplitIntAssertRangeHint` takes 1 operander as argument +// - `value` is the value that will be evaluated +// +// `newSplitIntAssertRangeHint` returns an error if `value` is not zero func newSplitIntAssertRangeHint(value hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "SplitIntAssertRange", @@ -437,6 +566,7 @@ func newSplitIntAssertRangeHint(value hinter.ResOperander) hinter.Hinter { if err != nil { return err } + if !value.IsZero() { return fmt.Errorf("assertion `split_int(): value is out of range` failed") } @@ -451,9 +581,21 @@ func createSplitIntAssertRangeHinter(resolver hintReferenceResolver) (hinter.Hin if err != nil { return nil, err } + return newSplitIntAssertRangeHint(value), nil } +// SplitIntHint hint splits an integer in limbs based on a base +// and asserts that each limb is within a certain range +// +// `newSplitIntHint` takes 4 operanders as arguments +// - `output` is the location in memory where to write the result +// - `value` is the value to split +// - `base` is the base used to split the value in limbs +// - `bound` represents the maximum value for each limb +// +// `newSplitIntHint` is recursively called when splitting an integer +// and writes to the `output` memory address the calculated limb func newSplitIntHint(output, value, base, bound hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "SplitIntHint", @@ -487,6 +629,7 @@ func newSplitIntHint(output, value, base, bound hinter.ResOperander) hinter.Hint } v := memory.MemoryValueFromFieldElement(&result) + return vm.Memory.WriteToAddress(&outputAddr, &v) }, } @@ -497,21 +640,34 @@ func createSplitIntHinter(resolver hintReferenceResolver) (hinter.Hinter, error) if err != nil { return nil, err } + value, err := resolver.GetResOperander("value") if err != nil { return nil, err } + base, err := resolver.GetResOperander("base") if err != nil { return nil, err } + bound, err := resolver.GetResOperander("bound") if err != nil { return nil, err } + return newSplitIntHint(output, value, base, bound), nil } +// Pow hint calculates the least significant bit of the exponent +// of a number within a prime field +// +// `newPowHint` takes 2 operanders as arguments +// - `locs` is the variable that will store the result +// - `prevLocs` is the the variable used to calculate the exponent +// +// `newPowHint` writes to the memory address of `locs` variable the value of the least +// significant bit of the exponent of `prevLocs` variable module a prime field func newPowHint(locs, prevLocs hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "Pow", @@ -525,27 +681,33 @@ func newPowHint(locs, prevLocs hinter.ResOperander) hinter.Hinter { 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) }, } @@ -556,15 +718,26 @@ func createPowHinter(resolver hintReferenceResolver) (hinter.Hinter, error) { 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 } +// SplitFelt hint splits a finite field element into high and low components +// +// `newSplitFeltHint` takes 3 operanders as arguments +// - `low` and `high` are the variables that will store the low and high components +// - `value` is the variable to split +// +// `newSplitFeltHint` writes the low and high components in the `low` and `high` +// memory address, respectively func newSplitFeltHint(low, high, value hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "SplitFelt", @@ -615,6 +788,7 @@ func newSplitFeltHint(low, high, value hinter.ResOperander) hinter.Hinter { if err != nil { return err } + //> ids.high = ids.value >> 128 highBigInt := new(big.Int).Rsh(&valueBigInt, 128) highValue := memory.MemoryValueFromFieldElement(new(fp.Element).SetBigInt(highBigInt)) @@ -644,6 +818,17 @@ func createSplitFeltHinter(resolver hintReferenceResolver) (hinter.Hinter, error return newSplitFeltHint(low, high, value), nil } +// SignedDivRem hint computes a signed division and modulus operation on a given value, +// ensuring the quotient is within a specified range +// +// `newSignedDivRemHint` takes 5 operanders as arguments +// - `value` is the dividende of the operation +// - `div` is the divisor of the operation +// - `bound` is the upper limit of the specified range +// - `r` is the variable that will store the remainder of the modular division +// - `biased_q` is the variable that will store the quotient of the modular division +// +// `newSignedDivRemHint` writes `r` and `biased_q` values to their respective memory address func newSignedDivRemHint(value, div, bound, r, biased_q hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "SignedDivRem", @@ -663,9 +848,10 @@ func newSignedDivRemHint(value, div, bound, r, biased_q hinter.ResOperander) hin if err != nil { return err } + //> assert 0 < ids.div <= PRIME // range_check_builtin.bound, f'div={hex(ids.div)} is out of the valid range.' if divFelt.IsZero() || !utils.FeltLe(divFelt, &utils.PrimeHigh) { - return fmt.Errorf("div=%v is out of the valid range.", divFelt) + return fmt.Errorf("div=%v is out of the valid range", divFelt) } //> assert_integer(ids.bound) @@ -673,15 +859,18 @@ func newSignedDivRemHint(value, div, bound, r, biased_q hinter.ResOperander) hin if err != nil { return err } + //> assert ids.bound <= range_check_builtin.bound // 2, f'bound={hex(ids.bound)} is out of the valid range.' if !utils.FeltLe(boundFelt, &utils.Felt127) { - return fmt.Errorf("bound=%v is out of the valid range.", boundFelt) + return fmt.Errorf("bound=%v is out of the valid range", boundFelt) } + //> int_value = as_int(ids.value, PRIME) valueFelt, err := hinter.ResolveAsFelt(vm, value) if err != nil { return err } + intValueBig := math_utils.AsInt(valueFelt) //> q, ids.r = divmod(int_value, ids.div) @@ -702,7 +891,7 @@ func newSignedDivRemHint(value, div, bound, r, biased_q hinter.ResOperander) hin //> assert -ids.bound <= q < ids.bound, f'{int_value} / {ids.div} = {q} is out of the range [{-ids.bound}, {ids.bound}).' if !(qBig.Cmp(new(big.Int).Neg(&boundBig)) >= 0 && qBig.Cmp(&boundBig) == -1) { - return fmt.Errorf("%v / %v = %v is out of the range [-%v, %v].", valueFelt, divFelt, qBig, boundFelt, boundFelt) + return fmt.Errorf("%v / %v = %v is out of the range [-%v, %v]", valueFelt, divFelt, qBig, boundFelt, boundFelt) } //> ids.biased_q = q + ids.bound @@ -723,26 +912,38 @@ func createSignedDivRemHinter(resolver hintReferenceResolver) (hinter.Hinter, er if err != nil { return nil, err } + div, err := resolver.GetResOperander("div") if err != nil { return nil, err } + bound, err := resolver.GetResOperander("bound") if err != nil { return nil, err } + r, err := resolver.GetResOperander("r") if err != nil { return nil, err } + biased_q, err := resolver.GetResOperander("biased_q") if err != nil { return nil, err } - return newSignedDivRemHint(value, div, bound, r, biased_q), nil + return newSignedDivRemHint(value, div, bound, r, biased_q), nil } +// Sqrt hint computes the square root of a given value, +// ensuring it falls within a specific range +// +// `newSqrtHint` takes 2 operanders as arguments +// - `root` is the variable that will store the result +// - `value` is the variable to operate on +// +// `newSqrtHint` writes the result of the hint at `root` address in memory func newSqrtHint(root, value hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "Sqrt", @@ -775,6 +976,7 @@ func newSqrtHint(root, value hinter.ResOperander) hinter.Hinter { result.SetBytes(valueU256.Bytes()) v := memory.MemoryValueFromFieldElement(&result) + return vm.Memory.WriteToAddress(&rootAddr, &v) }, } @@ -785,14 +987,27 @@ func createSqrtHinter(resolver hintReferenceResolver) (hinter.Hinter, error) { if err != nil { return nil, err } + value, err := resolver.GetResOperander("value") if err != nil { return nil, err } + return newSqrtHint(root, value), nil } -func newUnsignedDivRemHinter(value, div, q, r hinter.ResOperander) hinter.Hinter { +// UnsignedDivRem hint computes an unsigned division and modulus +// operation on a given dividend and divisor +// +// `newUnsignedDivRemHinter` takes 4 operanders as arguments +// - `value` is the dividend of the operation +// - `div` is the divisor of the operation +// - `q` is the variable that will store the quotient of the modular division +// - `r` is the variable that will store the remainder of the modular division +// +// `newUnsignedDivRemHinter` writes `q` and `r` values to their respective memory address + +func newUnsignedDivRemHint(value, div, q, r hinter.ResOperander) hinter.Hinter { return &GenericZeroHinter{ Name: "UnsignedDivRem", Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { @@ -806,6 +1021,7 @@ func newUnsignedDivRemHinter(value, div, q, r hinter.ResOperander) hinter.Hinter if err != nil { return err } + div, err := hinter.ResolveAsFelt(vm, div) if err != nil { return err @@ -815,10 +1031,12 @@ func newUnsignedDivRemHinter(value, div, q, r hinter.ResOperander) hinter.Hinter if err != nil { return err } + rAddr, err := r.GetAddress(vm) if err != nil { return err } + // (PRIME // range_check_builtin.bound) // 800000000000011000000000000000000000000000000000000000000000001 // 2**128 var divUpperBound big.Int @@ -828,7 +1046,7 @@ func newUnsignedDivRemHinter(value, div, q, r hinter.ResOperander) hinter.Hinter div.BigInt(&divBig) if div.IsZero() || divBig.Cmp(&divUpperBound) == 1 { - return fmt.Errorf("div=0x%v is out of the valid range.", divBig.Text(16)) + return fmt.Errorf("div=0x%v is out of the valid range", divBig.Text(16)) } q, r := utils.FeltDivRem(value, div) @@ -837,7 +1055,9 @@ func newUnsignedDivRemHinter(value, div, q, r hinter.ResOperander) hinter.Hinter if err := vm.Memory.WriteToAddress(&qAddr, &qValue); err != nil { return err } + rValue := memory.MemoryValueFromFieldElement(&r) + return vm.Memory.WriteToAddress(&rAddr, &rValue) }, } @@ -848,17 +1068,21 @@ func createUnsignedDivRemHinter(resolver hintReferenceResolver) (hinter.Hinter, if err != nil { return nil, err } + div, err := resolver.GetResOperander("div") if err != nil { return nil, err } + q, err := resolver.GetResOperander("q") if err != nil { return nil, err } + r, err := resolver.GetResOperander("r") if err != nil { return nil, err } - return newUnsignedDivRemHinter(value, div, q, r), nil + + return newUnsignedDivRemHint(value, div, q, r), nil } diff --git a/pkg/hintrunner/zero/zerohint_math_test.go b/pkg/hintrunner/zero/zerohint_math_test.go index 0bee291c0..9f7a55aa7 100644 --- a/pkg/hintrunner/zero/zerohint_math_test.go +++ b/pkg/hintrunner/zero/zerohint_math_test.go @@ -587,7 +587,7 @@ func TestZeroHintMath(t *testing.T) { makeHinter: func(ctx *hintTestContext) hinter.Hinter { return newSignedDivRemHint(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["bound"], ctx.operanders["r"], ctx.operanders["biased_q"]) }, - errCheck: errorTextContains(fmt.Sprintf("div=%v is out of the valid range.", &utils.FeltMax128)), + errCheck: errorTextContains(fmt.Sprintf("div=%v is out of the valid range", &utils.FeltMax128)), }, { operanders: []*hintOperander{ @@ -600,7 +600,7 @@ func TestZeroHintMath(t *testing.T) { makeHinter: func(ctx *hintTestContext) hinter.Hinter { return newSignedDivRemHint(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["bound"], ctx.operanders["r"], ctx.operanders["biased_q"]) }, - errCheck: errorTextContains(fmt.Sprintf("div=%v is out of the valid range.", &utils.FeltZero)), + errCheck: errorTextContains(fmt.Sprintf("div=%v is out of the valid range", &utils.FeltZero)), }, { operanders: []*hintOperander{ @@ -639,7 +639,7 @@ func TestZeroHintMath(t *testing.T) { makeHinter: func(ctx *hintTestContext) hinter.Hinter { return newSignedDivRemHint(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["bound"], ctx.operanders["r"], ctx.operanders["biased_q"]) }, - errCheck: errorTextContains(fmt.Sprintf("%v / %v = %v is out of the range [-%v, %v].", feltInt64(6), feltInt64(2), feltInt64(3), feltInt64(3), feltInt64(3))), + errCheck: errorTextContains(fmt.Sprintf("%v / %v = %v is out of the range [-%v, %v]", feltInt64(6), feltInt64(2), feltInt64(3), feltInt64(3), feltInt64(3))), }, { operanders: []*hintOperander{ @@ -725,7 +725,7 @@ func TestZeroHintMath(t *testing.T) { {Name: "q", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 1)}, }, makeHinter: func(ctx *hintTestContext) hinter.Hinter { - return newUnsignedDivRemHinter(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) + return newUnsignedDivRemHint(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) }, check: allVarValueEquals(map[string]*fp.Element{ "q": feltInt64(16), @@ -740,7 +740,7 @@ func TestZeroHintMath(t *testing.T) { {Name: "q", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 1)}, }, makeHinter: func(ctx *hintTestContext) hinter.Hinter { - return newUnsignedDivRemHinter(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) + return newUnsignedDivRemHint(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) }, check: allVarValueEquals(map[string]*fp.Element{ "q": feltInt64(3), @@ -755,7 +755,7 @@ func TestZeroHintMath(t *testing.T) { {Name: "q", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 1)}, }, makeHinter: func(ctx *hintTestContext) hinter.Hinter { - return newUnsignedDivRemHinter(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) + return newUnsignedDivRemHint(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) }, check: allVarValueEquals(map[string]*fp.Element{ "q": feltInt64(0), @@ -770,9 +770,9 @@ func TestZeroHintMath(t *testing.T) { {Name: "q", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 1)}, }, makeHinter: func(ctx *hintTestContext) hinter.Hinter { - return newUnsignedDivRemHinter(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) + return newUnsignedDivRemHint(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) }, - errCheck: errorTextContains("div=0x0 is out of the valid range."), + errCheck: errorTextContains("div=0x0 is out of the valid range"), }, { operanders: []*hintOperander{ @@ -782,7 +782,7 @@ func TestZeroHintMath(t *testing.T) { {Name: "q", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 1)}, }, makeHinter: func(ctx *hintTestContext) hinter.Hinter { - return newUnsignedDivRemHinter(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) + return newUnsignedDivRemHint(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) }, check: allVarValueEquals(map[string]*fp.Element{ "q": feltInt64(0), @@ -797,7 +797,7 @@ func TestZeroHintMath(t *testing.T) { {Name: "q", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 1)}, }, makeHinter: func(ctx *hintTestContext) hinter.Hinter { - return newUnsignedDivRemHinter(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) + return newUnsignedDivRemHint(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) }, check: allVarValueEquals(map[string]*fp.Element{ "q": feltInt64(0), @@ -812,9 +812,9 @@ func TestZeroHintMath(t *testing.T) { {Name: "q", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 1)}, }, makeHinter: func(ctx *hintTestContext) hinter.Hinter { - return newUnsignedDivRemHinter(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) + return newUnsignedDivRemHint(ctx.operanders["value"], ctx.operanders["div"], ctx.operanders["q"], ctx.operanders["r"]) }, - errCheck: errorTextContains("div=0x8000000000000110000000000000001 is out of the valid range."), + errCheck: errorTextContains("div=0x8000000000000110000000000000001 is out of the valid range"), }, }, })