package benchmarks

import (
	"strconv"
	"sync/atomic"
	"testing"
)

// -----------------------------------------------------------------------------
// 1. Data Locality: Matrix Traversal
// -----------------------------------------------------------------------------

const (
	rows = 1000
	cols = 1000
)

var matrix [rows][cols]int

func init() {
	for r := 0; r < rows; r++ {
		for c := 0; c < cols; c++ {
			matrix[r][c] = r + c
		}
	}
	// Initialize large array to prevent optimization
	for i := range largeList {
		largeList[i] = byte(i)
	}
}

// BenchmarkMatrixRowTraversal iterates over the matrix row by row.
func BenchmarkMatrixRowTraversal(b *testing.B) {
	var sum int
	for i := 0; i < b.N; i++ {
		sum = 0
		for r := 0; r < rows; r++ {
			for c := 0; c < cols; c++ {
				sum += matrix[r][c]
			}
		}
	}
	_ = sum
}

// BenchmarkMatrixColTraversal iterates over the matrix column by column.
func BenchmarkMatrixColTraversal(b *testing.B) {
	var sum int
	for i := 0; i < b.N; i++ {
		sum = 0
		for c := 0; c < cols; c++ {
			for r := 0; r < rows; r++ {
				sum += matrix[r][c]
			}
		}
	}
	_ = sum
}

// -----------------------------------------------------------------------------
// 2. False Sharing
// -----------------------------------------------------------------------------

type NoPad struct {
	a uint64
	b uint64
}

type WithPad struct {
	a uint64
	_ [56]byte // Padding to fill a 64-byte cache line
	b uint64
}

func BenchmarkFalseSharing(b *testing.B) {
	s := &NoPad{}
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			atomic.AddUint64(&s.a, 1)
			atomic.AddUint64(&s.b, 1)
		}
	})
}

func BenchmarkNoFalseSharing(b *testing.B) {
	s := &WithPad{}
	b.RunParallel(func(pb *testing.PB) {
		for pb.Next() {
			atomic.AddUint64(&s.a, 1)
			atomic.AddUint64(&s.b, 1)
		}
	})
}

// -----------------------------------------------------------------------------
// 3. Cache Line Striding / Latency Simulation
// -----------------------------------------------------------------------------

const largeArraySize = 64 * 1024 * 1024 // 64MB
var largeList = make([]byte, largeArraySize)

func BenchmarkStrideWalk(b *testing.B) {
	strides := []int{64, 256, 1024, 4 * 1024, 64 * 1024, 256 * 1024, 1024 * 1024}

	for _, stride := range strides {
		b.Run("Stride_"+strconv.Itoa(stride), func(b *testing.B) {
			var accumulator byte
			length := len(largeList)
			mask := length - 1

			// Use a local loop to ensure compiler doesn't optimize away too much
			// and to keep the math simple.
			for i := 0; i < b.N; i++ {
				idx := (i * stride) & mask
				accumulator += largeList[idx]
			}
			_ = accumulator
		})
	}
}
