package main

import (
	"strconv"
	"testing"
)

// section: benchmarks

// BenchmarkMapWithoutSizeHint demonstrates map creation without pre-allocation
func BenchmarkMapWithoutSizeHint_Small(b *testing.B) {
	// Pre-generate keys to exclude strconv.Itoa() overhead
	keys := make([]string, 100)
	for i := 0; i < 100; i++ {
		keys[i] = strconv.Itoa(i)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m := make(map[string]int)
		for j := 0; j < 100; j++ {
			m[keys[j]] = j
		}
	}
}

func BenchmarkMapWithoutSizeHint_Medium(b *testing.B) {
	// Pre-generate keys to exclude strconv.Itoa() overhead
	keys := make([]string, 1000)
	for i := 0; i < 1000; i++ {
		keys[i] = strconv.Itoa(i)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m := make(map[string]int)
		for j := 0; j < 1000; j++ {
			m[keys[j]] = j
		}
	}
}

func BenchmarkMapWithoutSizeHint_Large(b *testing.B) {
	// Pre-generate keys to exclude strconv.Itoa() overhead
	keys := make([]string, 10000)
	for i := 0; i < 10000; i++ {
		keys[i] = strconv.Itoa(i)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m := make(map[string]int)
		for j := 0; j < 10000; j++ {
			m[keys[j]] = j
		}
	}
}

// BenchmarkMapWithSizeHint demonstrates map creation with pre-allocated size
func BenchmarkMapWithSizeHint_Small(b *testing.B) {
	// Pre-generate keys to exclude strconv.Itoa() overhead
	keys := make([]string, 100)
	for i := 0; i < 100; i++ {
		keys[i] = strconv.Itoa(i)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m := make(map[string]int, 100)
		for j := 0; j < 100; j++ {
			m[keys[j]] = j
		}
	}
}

func BenchmarkMapWithSizeHint_Medium(b *testing.B) {
	// Pre-generate keys to exclude strconv.Itoa() overhead
	keys := make([]string, 1000)
	for i := 0; i < 1000; i++ {
		keys[i] = strconv.Itoa(i)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m := make(map[string]int, 1000)
		for j := 0; j < 1000; j++ {
			m[keys[j]] = j
		}
	}
}

func BenchmarkMapWithSizeHint_Large(b *testing.B) {
	// Pre-generate keys to exclude strconv.Itoa() overhead
	keys := make([]string, 10000)
	for i := 0; i < 10000; i++ {
		keys[i] = strconv.Itoa(i)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m := make(map[string]int, 10000)
		for j := 0; j < 10000; j++ {
			m[keys[j]] = j
		}
	}
}

// BenchmarkMapLookup demonstrates lookup performance
func BenchmarkMapLookup_Exists(b *testing.B) {
	m := make(map[string]int, 10000)
	keys := make([]string, 10000)
	for i := 0; i < 10000; i++ {
		keys[i] = strconv.Itoa(i)
		m[keys[i]] = i
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = m[keys[i%10000]]
	}
}

func BenchmarkMapLookup_NotExists(b *testing.B) {
	m := make(map[string]int, 10000)
	for i := 0; i < 10000; i++ {
		m[strconv.Itoa(i)] = i
	}

	// Pre-generate non-existent keys
	nonExistentKeys := make([]string, 10000)
	for i := 0; i < 10000; i++ {
		nonExistentKeys[i] = strconv.Itoa(i + 10000)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_ = m[nonExistentKeys[i%10000]]
	}
}

func BenchmarkMapLookup_WithOkCheck(b *testing.B) {
	m := make(map[string]int, 10000)
	keys := make([]string, 10000)
	for i := 0; i < 10000; i++ {
		keys[i] = strconv.Itoa(i)
		m[keys[i]] = i
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		_, ok := m[keys[i%10000]]
		_ = ok
	}
}

// BenchmarkMapIteration demonstrates iteration performance
func BenchmarkMapIteration_Small(b *testing.B) {
	m := make(map[string]int, 100)
	for i := 0; i < 100; i++ {
		m[strconv.Itoa(i)] = i
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sum := 0
		for _, v := range m {
			sum += v
		}
		_ = sum
	}
}

func BenchmarkMapIteration_Large(b *testing.B) {
	m := make(map[string]int, 10000)
	for i := 0; i < 10000; i++ {
		m[strconv.Itoa(i)] = i
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		sum := 0
		for _, v := range m {
			sum += v
		}
		_ = sum
	}
}

// BenchmarkMapDelete demonstrates delete performance
func BenchmarkMapDelete(b *testing.B) {
	// Pre-generate keys to exclude strconv.Itoa() overhead
	keys := make([]string, 1000)
	for i := 0; i < 1000; i++ {
		keys[i] = strconv.Itoa(i)
	}

	for i := 0; i < b.N; i++ {
		b.StopTimer()
		m := make(map[string]int, 1000)
		for j := 0; j < 1000; j++ {
			m[keys[j]] = j
		}
		b.StartTimer()

		for j := 0; j < 1000; j++ {
			delete(m, keys[j])
		}
	}
}

// BenchmarkMapStringVsIntKey compares string vs int keys
func BenchmarkMapStringKeys(b *testing.B) {
	// Pre-generate string keys to isolate map performance from conversion overhead
	keys := make([]string, 1000)
	for i := 0; i < 1000; i++ {
		keys[i] = strconv.Itoa(i)
	}

	b.ResetTimer()
	for i := 0; i < b.N; i++ {
		m := make(map[string]int, 1000)
		for j := 0; j < 1000; j++ {
			m[keys[j]] = j
		}
	}
}

func BenchmarkMapIntKeys(b *testing.B) {
	for i := 0; i < b.N; i++ {
		m := make(map[int]int, 1000)
		for j := 0; j < 1000; j++ {
			m[j] = j
		}
	}
}

// BenchmarkMapClear demonstrates different clearing strategies
func BenchmarkMapClear_Delete(b *testing.B) {
	// Pre-generate keys to exclude strconv.Itoa() overhead
	keys := make([]string, 1000)
	for i := 0; i < 1000; i++ {
		keys[i] = strconv.Itoa(i)
	}

	for i := 0; i < b.N; i++ {
		b.StopTimer()
		m := make(map[string]int, 1000)
		for j := 0; j < 1000; j++ {
			m[keys[j]] = j
		}
		b.StartTimer()

		for k := range m {
			delete(m, k)
		}
	}
}

func BenchmarkMapClear_Recreate(b *testing.B) {
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		m := make(map[string]int, 1000)
		for j := 0; j < 1000; j++ {
			m[strconv.Itoa(j)] = j
		}
		b.StartTimer()

		m = make(map[string]int, 1000)
		_ = m
	}
}

// Go 1.21+ includes clear() built-in function
func BenchmarkMapClear_BuiltIn(b *testing.B) {
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		m := make(map[string]int, 1000)
		for j := 0; j < 1000; j++ {
			m[strconv.Itoa(j)] = j
		}
		b.StartTimer()

		clear(m)
	}
}

// section: benchmarks
