Skip to content

Commit

Permalink
Implement UnsignedDivRem hint (#311)
Browse files Browse the repository at this point in the history
* Implement UnsignedDivRem hint

* Rename hinter methods for UnsignedDivRem

* Fix divUpperBound calculation + slight refactoring

* Fix tests

* Fix import style
  • Loading branch information
har777 committed Mar 20, 2024
1 parent 41bd195 commit 0e63d86
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 0 deletions.
2 changes: 2 additions & 0 deletions pkg/hintrunner/zero/hintcode.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ 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.'"

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.
splitFeltCode string = "from starkware.cairo.common.math_utils import assert_integer\nassert ids.MAX_HIGH < 2**128 and ids.MAX_LOW < 2**128\nassert PRIME - 1 == ids.MAX_HIGH * 2**128 + ids.MAX_LOW\nassert_integer(ids.value)\nids.low = ids.value & ((1 << 128) - 1)\nids.high = ids.value >> 128"

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 @@ -104,6 +104,8 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64
return createUint256MulDivModHinter(resolver)
case sqrtCode:
return createSqrtHinter(resolver)
case unsignedDivRemCode:
return createUnsignedDivRemHinter(resolver)
default:
return nil, fmt.Errorf("Not identified hint")
}
Expand Down
73 changes: 73 additions & 0 deletions pkg/hintrunner/zero/zerohint_math.go
Original file line number Diff line number Diff line change
Expand Up @@ -633,3 +633,76 @@ func createSqrtHinter(resolver hintReferenceResolver) (hinter.Hinter, error) {
}
return newSqrtHint(root, value), nil
}

func newUnsignedDivRemHinter(value, div, q, r hinter.ResOperander) hinter.Hinter {
return &GenericZeroHinter{
Name: "UnsignedDivRem",
Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error {
//> from starkware.cairo.common.math_utils import assert_integer
//> assert_integer(ids.div)
//> assert 0 < ids.div <= PRIME // range_check_builtin.bound, \
//> f'div={hex(ids.div)} is out of the valid range.'
//> ids.q, ids.r = divmod(ids.value, ids.div)

value, err := hinter.ResolveAsFelt(vm, value)
if err != nil {
return err
}
div, err := hinter.ResolveAsFelt(vm, div)
if err != nil {
return err
}

qAddr, err := q.GetAddress(vm)
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
divUpperBound.SetString("8000000000000110000000000000000", 16)

var divBig big.Int
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))
}

q, r := utils.FeltDivRem(value, div)

qValue := memory.MemoryValueFromFieldElement(&q)
if err := vm.Memory.WriteToAddress(&qAddr, &qValue); err != nil {
return err
}
rValue := memory.MemoryValueFromFieldElement(&r)
return vm.Memory.WriteToAddress(&rAddr, &rValue)
},
}
}

func createUnsignedDivRemHinter(resolver hintReferenceResolver) (hinter.Hinter, error) {
value, err := resolver.GetResOperander("value")
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
}
102 changes: 102 additions & 0 deletions pkg/hintrunner/zero/zerohint_math_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -629,5 +629,107 @@ func TestZeroHintMath(t *testing.T) {
errCheck: errorTextContains("outside of the range [0, 2**250)"),
},
},

"UnsignedDivRem": {
{
operanders: []*hintOperander{
{Name: "value", Kind: fpRelative, Value: feltUint64(100)},
{Name: "div", Kind: fpRelative, Value: feltUint64(6)},
{Name: "r", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 0)},
{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"])
},
check: allVarValueEquals(map[string]*fp.Element{
"q": feltInt64(16),
"r": feltInt64(4),
}),
},
{
operanders: []*hintOperander{
{Name: "value", Kind: fpRelative, Value: feltUint64(450326666)},
{Name: "div", Kind: fpRelative, Value: feltUint64(136310839)},
{Name: "r", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 0)},
{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"])
},
check: allVarValueEquals(map[string]*fp.Element{
"q": feltInt64(3),
"r": feltInt64(41394149),
}),
},
{
operanders: []*hintOperander{
{Name: "value", Kind: fpRelative, Value: feltUint64(0)},
{Name: "div", Kind: fpRelative, Value: feltUint64(10)},
{Name: "r", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 0)},
{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"])
},
check: allVarValueEquals(map[string]*fp.Element{
"q": feltInt64(0),
"r": feltInt64(0),
}),
},
{
operanders: []*hintOperander{
{Name: "value", Kind: fpRelative, Value: feltUint64(10)},
{Name: "div", Kind: fpRelative, Value: feltUint64(0)},
{Name: "r", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 0)},
{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"])
},
errCheck: errorTextContains("div=0x0 is out of the valid range."),
},
{
operanders: []*hintOperander{
{Name: "value", Kind: fpRelative, Value: feltUint64(10)},
{Name: "div", Kind: fpRelative, Value: feltString("10633823966279327296825105735305134079")},
{Name: "r", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 0)},
{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"])
},
check: allVarValueEquals(map[string]*fp.Element{
"q": feltInt64(0),
"r": feltInt64(10),
}),
},
{
operanders: []*hintOperander{
{Name: "value", Kind: fpRelative, Value: feltUint64(10)},
{Name: "div", Kind: fpRelative, Value: feltString("10633823966279327296825105735305134080")},
{Name: "r", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 0)},
{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"])
},
check: allVarValueEquals(map[string]*fp.Element{
"q": feltInt64(0),
"r": feltInt64(10),
}),
},
{
operanders: []*hintOperander{
{Name: "value", Kind: fpRelative, Value: feltUint64(10)},
{Name: "div", Kind: fpRelative, Value: feltString("10633823966279327296825105735305134081")},
{Name: "r", Kind: reference, Value: addrBuiltin(starknet.RangeCheck, 0)},
{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"])
},
errCheck: errorTextContains("div=0x8000000000000110000000000000001 is out of the valid range."),
},
},
})
}

0 comments on commit 0e63d86

Please sign in to comment.