From 0e63d86efcca5af6f896332db5ba4f8f9bd657bb Mon Sep 17 00:00:00 2001 From: Harikrishnan Shaji Date: Wed, 20 Mar 2024 20:53:39 +0530 Subject: [PATCH] Implement UnsignedDivRem hint (#311) * Implement UnsignedDivRem hint * Rename hinter methods for UnsignedDivRem * Fix divUpperBound calculation + slight refactoring * Fix tests * Fix import style --- pkg/hintrunner/zero/hintcode.go | 2 + pkg/hintrunner/zero/zerohint.go | 2 + pkg/hintrunner/zero/zerohint_math.go | 73 ++++++++++++++++ pkg/hintrunner/zero/zerohint_math_test.go | 102 ++++++++++++++++++++++ 4 files changed, 179 insertions(+) diff --git a/pkg/hintrunner/zero/hintcode.go b/pkg/hintrunner/zero/hintcode.go index 2fd6f5b30..f1499e3ce 100644 --- a/pkg/hintrunner/zero/hintcode.go +++ b/pkg/hintrunner/zero/hintcode.go @@ -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" diff --git a/pkg/hintrunner/zero/zerohint.go b/pkg/hintrunner/zero/zerohint.go index d2a2d8e53..cbb559bc1 100644 --- a/pkg/hintrunner/zero/zerohint.go +++ b/pkg/hintrunner/zero/zerohint.go @@ -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") } diff --git a/pkg/hintrunner/zero/zerohint_math.go b/pkg/hintrunner/zero/zerohint_math.go index 0587f08f8..9e9e4bacf 100644 --- a/pkg/hintrunner/zero/zerohint_math.go +++ b/pkg/hintrunner/zero/zerohint_math.go @@ -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 +} diff --git a/pkg/hintrunner/zero/zerohint_math_test.go b/pkg/hintrunner/zero/zerohint_math_test.go index 475fad0b4..e2b477ed0 100644 --- a/pkg/hintrunner/zero/zerohint_math_test.go +++ b/pkg/hintrunner/zero/zerohint_math_test.go @@ -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."), + }, + }, }) }