diff --git a/pkg/hintrunner/zero/hintcode.go b/pkg/hintrunner/zero/hintcode.go index e697e2b16..34808f0cf 100644 --- a/pkg/hintrunner/zero/hintcode.go +++ b/pkg/hintrunner/zero/hintcode.go @@ -128,6 +128,7 @@ ids.multiplicities = segments.gen_arg([len(positions_dict[k]) for k in output])` compareBytesInWordCode string = "memory[ap] = to_felt_or_relocatable(ids.n_bytes < ids.BYTES_IN_WORD)" compareKeccakFullRateInBytesCode string = "memory[ap] = to_felt_or_relocatable(ids.n_bytes >= ids.KECCAK_FULL_RATE_IN_BYTES)" splitOutputMidLowHighCode string = "tmp, ids.output1_low = divmod(ids.output1, 256 ** 7)\nids.output1_high, ids.output1_mid = divmod(tmp, 2 ** 128)" + SplitNBytesCode string = "ids.n_words_to_copy, ids.n_bytes_left = divmod(ids.n_bytes, ids.BYTES_IN_WORD)" // ------ Dictionaries hints related code ------ dictNewCode string = "if '__dict_manager' not in globals():\n from starkware.cairo.common.dict import DictManager\n __dict_manager = DictManager()\n\nmemory[ap] = __dict_manager.new_dict(segments, initial_dict)\ndel initial_dict" diff --git a/pkg/hintrunner/zero/zerohint.go b/pkg/hintrunner/zero/zerohint.go index 26ffd6baf..f4fdc2149 100644 --- a/pkg/hintrunner/zero/zerohint.go +++ b/pkg/hintrunner/zero/zerohint.go @@ -175,6 +175,8 @@ func GetHintFromCode(program *zero.ZeroProgram, rawHint zero.Hint, hintPC uint64 return createCompareBytesInWordNondetHinter(resolver) case splitOutputMidLowHighCode: return createSplitOutputMidLowHighHinter(resolver) + case SplitNBytesCode: + return createSplitNBytesHinter(resolver) // Usort hints case usortEnterScopeCode: return createUsortEnterScopeHinter() diff --git a/pkg/hintrunner/zero/zerohint_keccak.go b/pkg/hintrunner/zero/zerohint_keccak.go index 4c27c6421..782adc8e0 100644 --- a/pkg/hintrunner/zero/zerohint_keccak.go +++ b/pkg/hintrunner/zero/zerohint_keccak.go @@ -317,9 +317,8 @@ func createUnsafeKeccakFinalizeHinter(resolver hintReferenceResolver) (hinter.Hi // - `low` is the low part of the `uint256` argument for the Keccac function // - `high` is the high part of the `uint256` argument for the Keccac function func newKeccakWriteArgsHint(inputs, low, high hinter.ResOperander) hinter.Hinter { - name := "KeccakWriteArgs" return &GenericZeroHinter{ - Name: name, + Name: "KeccakWriteArgs", Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { //> segments.write_arg(ids.inputs, [ids.low % 2 ** 64, ids.low // 2 ** 64]) //> segments.write_arg(ids.inputs + 2, [ids.high % 2 ** 64, ids.high // 2 ** 64]) @@ -460,9 +459,8 @@ func createCompareKeccakFullRateInBytesNondetHinter(resolver hintReferenceResolv // `newBlockPermutationHint` reads 25 memory cells starting from `keccakPtr - 25`, and writes // the result of the Keccak block permutation in the next 25 memory cells, starting from `keccakPtr` func newBlockPermutationHint(keccakPtr hinter.ResOperander) hinter.Hinter { - name := "BlockPermutation" return &GenericZeroHinter{ - Name: name, + Name: "BlockPermutation", Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { //> from starkware.cairo.common.keccak_utils.keccak_utils import keccak_func //> _keccak_state_size_felts = int(ids.KECCAK_STATE_SIZE_FELTS) @@ -674,3 +672,80 @@ func createSplitOutputMidLowHighHinter(resolver hintReferenceResolver) (hinter.H return newSplitOutputMidLowHighHint(output1, output1Low, output1Mid, output1High), nil } + +// SplitNBytes hint assigns to `ids.n_words_to_copy` and `ids.n_bytes_left` variables +// the quotient and remainder of the division of `ids.n_bytes` variable by the +// variable `ids.BYTES_IN_WORD` +// +// `newSplitNBytesHint` takes 3 operanders as arguments +// - `nWordsToCopy` is the variable that will store the quotient of the division +// - `nBytesLeft` is the variable that will store the remainder of the division +// - `nBytes` is the variable that will be divided +func newSplitNBytesHint(nBytes, nWordsToCopy, nBytesLeft hinter.ResOperander) hinter.Hinter { + name := "SplitNBytes" + return &GenericZeroHinter{ + Name: name, + Op: func(vm *VM.VirtualMachine, _ *hinter.HintRunnerContext) error { + //> ids.n_words_to_copy, ids.n_bytes_left = divmod(ids.n_bytes, ids.BYTES_IN_WORD) + + nWordsToCopyAddr, err := nWordsToCopy.GetAddress(vm) + if err != nil { + return err + } + + nBytesLeftAddr, err := nBytesLeft.GetAddress(vm) + if err != nil { + return err + } + + nBytesFelt, err := hinter.ResolveAsFelt(vm, nBytes) + if err != nil { + return err + } + + nBytesBigInt := new(big.Int) + nBytesFelt.BigInt(nBytesBigInt) + + bytesInWord := big.NewInt(8) + + nWordsToCopyBigInt := new(big.Int) + nBytesLeftBigInt := new(big.Int) + + nWordsToCopyBigInt.DivMod(nBytesBigInt, bytesInWord, nBytesLeftBigInt) + + var nWordsToCopyFelt fp.Element + nWordsToCopyFelt.SetBigInt(nWordsToCopyBigInt) + nWordsToCopyMv := memory.MemoryValueFromFieldElement(&nWordsToCopyFelt) + + var nBytesLeftFelt fp.Element + nBytesLeftFelt.SetBigInt(nBytesLeftBigInt) + nBytesLeftMv := memory.MemoryValueFromFieldElement(&nBytesLeftFelt) + + err = vm.Memory.WriteToAddress(&nBytesLeftAddr, &nBytesLeftMv) + if err != nil { + return err + } + + return vm.Memory.WriteToAddress(&nWordsToCopyAddr, &nWordsToCopyMv) + }, + } +} + +func createSplitNBytesHinter(resolver hintReferenceResolver) (hinter.Hinter, error) { + nBytes, err := resolver.GetResOperander("n_bytes") + if err != nil { + return nil, err + } + + nWordsToCopy, err := resolver.GetResOperander("n_words_to_copy") + if err != nil { + return nil, err + } + + nBytesLeft, err := resolver.GetResOperander("n_bytes_left") + if err != nil { + return nil, err + } + + return newSplitNBytesHint(nBytes, nWordsToCopy, nBytesLeft), nil +} diff --git a/pkg/hintrunner/zero/zerohint_keccak_test.go b/pkg/hintrunner/zero/zerohint_keccak_test.go index 03629ea3a..0db023683 100644 --- a/pkg/hintrunner/zero/zerohint_keccak_test.go +++ b/pkg/hintrunner/zero/zerohint_keccak_test.go @@ -703,5 +703,62 @@ func TestZeroHintKeccak(t *testing.T) { check: allVarValueEquals(map[string]*fp.Element{"output1_low": feltUint64(38756173112), "output1_mid": feltUint64(42), "output1_high": feltUint64(1)}), }, }, + "SplitNBytes": { + { + operanders: []*hintOperander{ + {Name: "n_bytes", Kind: apRelative, Value: feltUint64(0)}, + {Name: "n_words_to_copy", Kind: uninitialized}, + {Name: "n_bytes_left", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitNBytesHint(ctx.operanders["n_bytes"], ctx.operanders["n_words_to_copy"], ctx.operanders["n_bytes_left"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"n_words_to_copy": feltUint64(0), "n_bytes_left": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "n_bytes", Kind: apRelative, Value: feltUint64(45)}, + {Name: "n_words_to_copy", Kind: uninitialized}, + {Name: "n_bytes_left", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitNBytesHint(ctx.operanders["n_bytes"], ctx.operanders["n_words_to_copy"], ctx.operanders["n_bytes_left"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"n_words_to_copy": feltUint64(5), "n_bytes_left": feltUint64(5)}), + }, + { + operanders: []*hintOperander{ + {Name: "n_bytes", Kind: apRelative, Value: feltUint64(80)}, + {Name: "n_words_to_copy", Kind: uninitialized}, + {Name: "n_bytes_left", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitNBytesHint(ctx.operanders["n_bytes"], ctx.operanders["n_words_to_copy"], ctx.operanders["n_bytes_left"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"n_words_to_copy": feltUint64(10), "n_bytes_left": feltUint64(0)}), + }, + { + operanders: []*hintOperander{ + {Name: "n_bytes", Kind: apRelative, Value: feltUint64(7)}, + {Name: "n_words_to_copy", Kind: uninitialized}, + {Name: "n_bytes_left", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitNBytesHint(ctx.operanders["n_bytes"], ctx.operanders["n_words_to_copy"], ctx.operanders["n_bytes_left"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"n_words_to_copy": feltUint64(0), "n_bytes_left": feltUint64(7)}), + }, + { + operanders: []*hintOperander{ + {Name: "n_bytes", Kind: apRelative, Value: feltUint64(7523672657695691)}, + {Name: "n_words_to_copy", Kind: uninitialized}, + {Name: "n_bytes_left", Kind: uninitialized}, + }, + makeHinter: func(ctx *hintTestContext) hinter.Hinter { + return newSplitNBytesHint(ctx.operanders["n_bytes"], ctx.operanders["n_words_to_copy"], ctx.operanders["n_bytes_left"]) + }, + check: allVarValueEquals(map[string]*fp.Element{"n_words_to_copy": feltUint64(940459082211961), "n_bytes_left": feltUint64(3)}), + }, + }, }) }