// Copyright 2025 Buf Technologies, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Code generated by buf.build/go/hyperpb/internal/tools/hyperstencil. DO NOT EDIT.

package swiss_test

import (
	"buf.build/go/hyperpb/internal/arena"
	"buf.build/go/hyperpb/internal/swiss"
	"buf.build/go/hyperpb/internal/xunsafe"
	"fmt"
	"math/rand/v2"
	"slices"
	"strings"
	"testing"
)

func u32Benchmark(b *testing.B, c corpus[uint32], mapSize int) {
	_ = scalarBenchmark[uint32]
	b.Helper()

	r := rand.New(runtimeSource{})
	b.Run(strings.Trim(fmt.Sprintf("%Tx%d", c, mapSize), "*"), func(b *testing.B) {
		var extract func(uint32) []byte
		if e, ok := c.(extractor[uint32]); ok {
			extract = e.extract
		}

		theirsScalar := make(map[uint32]uint32)
		theirsString := make(map[string]uint32)

		entries := make([]swiss.Entry[uint32, uint32], mapSize)
		for i := range entries {
			k := c.sample(r, false)
			entries[i].Key = k
			entries[i].Value = uint32(i)
			if extract != nil {
				theirsString[xunsafe.SliceToString(extract(k))] = uint32(i)
			} else {
				theirsScalar[k] = uint32(i)
			}
		}
		_, ours := swiss.New(nil, extract, entries...)
		metrics := new(swiss.Metrics)
		if *benchProbes {
			ours.Record(metrics)
		}

		lookup := func(b *testing.B, miss bool) {
			b.Helper()

			lookup := make([]uint32, numKeys)
			for i := range lookup {
				lookup[i] = c.sample(r, miss)
			}

			b.Run("swiss", func(b *testing.B) {
				metrics.Reset()
				if extract != nil {
					for range b.N {
						for _, k := range lookup {
							ours.LookupFunc(extract(k), extract)
						}
					}
				} else {
					for range b.N {
						for _, k := range lookup {
							swiss.LookupU32xU32(ours, k)
						}
					}
				}

				if *benchProbes {
					metrics.Report(b)
				}
			})

			b.Run("gomap", func(b *testing.B) {
				if extract != nil {
					for range b.N {
						for _, k := range lookup {
							_ = theirsString[xunsafe.SliceToString(extract(k))]
						}
					}
				} else {
					for range b.N {
						for _, k := range lookup {
							_ = theirsScalar[k]
						}
					}
				}
			})
		}

		b.Run("hit", func(b *testing.B) { lookup(b, false) })
		b.Run("miss", func(b *testing.B) { lookup(b, true) })

		entries = slices.Repeat(entries, 10)
		r.Shuffle(len(entries), func(i, j int) {
			entries[i], entries[j] = entries[j], entries[i]
		})

		b.Run("build", func(b *testing.B) {
			b.Run("swiss", func(b *testing.B) {
				metrics := new(swiss.Metrics)
				arena := new(arena.Arena)
				arena.KeepAlive(metrics)

				b.ResetTimer()
				for range b.N {
					size, _ := swiss.Layout[uint32, uint32](0)
					m := xunsafe.Cast[swiss.Table[uint32, uint32]](arena.Alloc(size))
					swiss.InitU32xU32(m, 0, nil, extract)
					if *benchProbes {
						m.Record(metrics)
					}
					for _, kv := range entries {
						v := swiss.InsertU32xU32(m, kv.Key, extract)
						if v == nil {
							size, _ := swiss.Layout[uint32, uint32](m.Len() + 1)
							m2 := xunsafe.Cast[swiss.Table[uint32, uint32]](arena.Alloc(size))
							swiss.InitU32xU32(m2, m.Len()+1, m, extract)
							m = m2
							v = swiss.InsertU32xU32(m, kv.Key, extract)
						}
						*v = kv.Value
					}

					arena.Free()
				}

				if *benchProbes {
					metrics.Report(b)
				}
			})

			b.Run("gomap", func(b *testing.B) {
				if extract != nil {
					for range b.N {
						gomap := make(map[string]uint32)
						for _, kv := range entries {
							k := xunsafe.SliceToString(extract(kv.Key))
							gomap[k] = kv.Value
						}
					}
				} else {
					for range b.N {
						gomap := make(map[uint32]uint32)
						for _, kv := range entries {
							gomap[kv.Key] = kv.Value
						}
					}
				}
			})
		})
	})
}
func u64Benchmark(b *testing.B, c corpus[uint64], mapSize int) {
	_ = scalarBenchmark[uint64]
	b.Helper()

	r := rand.New(runtimeSource{})
	b.Run(strings.Trim(fmt.Sprintf("%Tx%d", c, mapSize), "*"), func(b *testing.B) {
		var extract func(uint64) []byte
		if e, ok := c.(extractor[uint64]); ok {
			extract = e.extract
		}

		theirsScalar := make(map[uint64]uint32)
		theirsString := make(map[string]uint32)

		entries := make([]swiss.Entry[uint64, uint32], mapSize)
		for i := range entries {
			k := c.sample(r, false)
			entries[i].Key = k
			entries[i].Value = uint32(i)
			if extract != nil {
				theirsString[xunsafe.SliceToString(extract(k))] = uint32(i)
			} else {
				theirsScalar[k] = uint32(i)
			}
		}
		_, ours := swiss.New(nil, extract, entries...)
		metrics := new(swiss.Metrics)
		if *benchProbes {
			ours.Record(metrics)
		}

		lookup := func(b *testing.B, miss bool) {
			b.Helper()

			lookup := make([]uint64, numKeys)
			for i := range lookup {
				lookup[i] = c.sample(r, miss)
			}

			b.Run("swiss", func(b *testing.B) {
				metrics.Reset()
				if extract != nil {
					for range b.N {
						for _, k := range lookup {
							ours.LookupFunc(extract(k), extract)
						}
					}
				} else {
					for range b.N {
						for _, k := range lookup {
							swiss.LookupU64xU32(ours, k)
						}
					}
				}

				if *benchProbes {
					metrics.Report(b)
				}
			})

			b.Run("gomap", func(b *testing.B) {
				if extract != nil {
					for range b.N {
						for _, k := range lookup {
							_ = theirsString[xunsafe.SliceToString(extract(k))]
						}
					}
				} else {
					for range b.N {
						for _, k := range lookup {
							_ = theirsScalar[k]
						}
					}
				}
			})
		}

		b.Run("hit", func(b *testing.B) { lookup(b, false) })
		b.Run("miss", func(b *testing.B) { lookup(b, true) })

		entries = slices.Repeat(entries, 10)
		r.Shuffle(len(entries), func(i, j int) {
			entries[i], entries[j] = entries[j], entries[i]
		})

		b.Run("build", func(b *testing.B) {
			b.Run("swiss", func(b *testing.B) {
				metrics := new(swiss.Metrics)
				arena := new(arena.Arena)
				arena.KeepAlive(metrics)

				b.ResetTimer()
				for range b.N {
					size, _ := swiss.Layout[uint64, uint32](0)
					m := xunsafe.Cast[swiss.Table[uint64, uint32]](arena.Alloc(size))
					swiss.InitU64xU32(m, 0, nil, extract)
					if *benchProbes {
						m.Record(metrics)
					}
					for _, kv := range entries {
						v := swiss.InsertU64xU32(m, kv.Key, extract)
						if v == nil {
							size, _ := swiss.Layout[uint64, uint32](m.Len() + 1)
							m2 := xunsafe.Cast[swiss.Table[uint64, uint32]](arena.Alloc(size))
							swiss.InitU64xU32(m2, m.Len()+1, m, extract)
							m = m2
							v = swiss.InsertU64xU32(m, kv.Key, extract)
						}
						*v = kv.Value
					}

					arena.Free()
				}

				if *benchProbes {
					metrics.Report(b)
				}
			})

			b.Run("gomap", func(b *testing.B) {
				if extract != nil {
					for range b.N {
						gomap := make(map[string]uint32)
						for _, kv := range entries {
							k := xunsafe.SliceToString(extract(kv.Key))
							gomap[k] = kv.Value
						}
					}
				} else {
					for range b.N {
						gomap := make(map[uint64]uint32)
						for _, kv := range entries {
							gomap[kv.Key] = kv.Value
						}
					}
				}
			})
		})
	})
}
