// Copyright 2021 Molecula Corp. All rights reserved.
package ingest

import (
	"testing"
)

const sampleSize = 1000000

var sampleSortingData = createSampleFieldData()

func createSampleFieldData() *FieldOperation {
	fo := &FieldOperation{}
	fo.RecordIDs = make([]uint64, sampleSize)
	fo.Values = make([]uint64, sampleSize)
	fo.Signed = make([]int64, sampleSize)
	for i := range fo.RecordIDs {
		fo.RecordIDs[i] = (uint64(i) * 63 * 3456789) % 50000000
		fo.Values[i] = (uint64(i) * 6) % 8
		fo.Signed[i] = ((int64(i) * 17) % 15) - 8
	}
	return fo
}

func benchmarkOneSort(b *testing.B, fo *FieldOperation) {
	for i := 0; i < b.N; i++ {
		b.StopTimer()
		sortable := fo.clone()
		b.StartTimer()
		_ = sortable.SortToShards()
	}
}

func BenchmarkSortFieldOp(b *testing.B) {
	b.Run("full", func(b *testing.B) {
		f2 := *sampleSortingData
		benchmarkOneSort(b, &f2)
	})
	b.Run("nosign", func(b *testing.B) {
		f2 := *sampleSortingData
		f2.Signed = nil
		benchmarkOneSort(b, &f2)
	})
	b.Run("signonly", func(b *testing.B) {
		f2 := *sampleSortingData
		f2.Values = nil
		benchmarkOneSort(b, &f2)
	})
}

// func BenchmarkSort64(b *testing.B) {
// 	gen := func(n, width uint64) func() []uint64 {
// 		var data []uint64
// 		var once sync.Once
// 		return func() []uint64 {
// 			once.Do(func() {
// 				data = make([]uint64, n)
// 				var rng rand.PCGSource
// 				rng.Seed(9001)
// 				for i := range data {
// 					data[i] = rng.Uint64() % width
// 				}
// 			})
//
// 			return data
// 		}
// 	}
//
// 	algos := []struct {
// 		name string
// 		maxn uint64
// 		fn   func([]uint64) []uint64
// 	}{
// 		{
// 			name: "stdlib",
// 			maxn: 1024 * 1024 * 1024,
// 			fn:   stdSort,
// 		},
// 		{
// 			name: "heap",
// 			maxn: 1024 * 1024 * 1024,
// 			fn:   heapSort,
// 		},
// 		{
// 			name: "radix-insertion-mask",
// 			maxn: 1024 * 1024 * 1024,
// 			fn:   dedupSort64,
// 		},
// 	}
//
// 	widths := []struct {
// 		name  string
// 		width uint64
// 	}{
// 		{"64", 64},
// 		{"1K", 1024},
// 		{"64K", 64 * 1024},
// 		{"1M", 1024 * 1024},
// 		{"16M", 16 * 1024 * 1024},
// 		{"128M", 128 * 1024 * 1024},
// 		{"1B", 1024 * 1024 * 1024},
// 	}
//
// 	counts := []struct {
// 		name string
// 		n    uint64
// 	}{
// 		{"64", 64},
// 		{"1K", 1024},
// 		{"64K", 64 * 1024},
// 		{"1M", 1024 * 1024},
// 		{"4M", 4 * 1024 * 1024},
// 		{"16M", 16 * 1024 * 1024},
// 		{"64M", 64 * 1024 * 1024},
// 		{"256M", 256 * 1024 * 1024},
// 	}
//
// 	for _, width := range widths {
// 		width := width
// 		b.Run(width.name, func(b *testing.B) {
// 			for _, count := range counts {
// 				if count.n > width.width {
// 					continue
// 				}
//
// 				count := count
// 				b.Run(count.name, func(b *testing.B) {
// 					datasrc := gen(count.n, width.width)
// 					for _, alg := range algos {
// 						if count.n > alg.maxn {
// 							continue
// 						}
//
// 						alg := alg
// 						b.Run(alg.name, func(b *testing.B) {
// 							data := datasrc()
// 							buf := make([]uint64, len(data))
// 							b.SetBytes(8 * int64(len(buf)))
//
// 							b.StopTimer()
// 							b.ResetTimer()
//
// 							for i := 0; i < b.N; i++ {
// 								copy(buf, data)
// 								b.StartTimer()
// 								alg.fn(buf)
// 								b.StopTimer()
// 							}
// 						})
// 					}
// 				})
// 			}
// 		})
// 	}
// }
//
// func heapSort(data []uint64) []uint64 {
// 	for i, v := range data {
// 		for i > 0 && v > data[(i-1)/2] {
// 			data[i] = data[(i-1)/2]
// 			i = (i - 1) / 2
// 		}
// 		data[i] = v
// 	}
// 	{
// 		heap := data
// 		for len(heap) > 1 {
// 			heap[0], heap[len(heap)-1] = heap[len(heap)-1], heap[0]
// 			heap = heap[:len(heap)-1]
// 			i := 0
// 			for {
// 				max := i
// 				if r := 2*i + 1; r < len(heap) && heap[r] > heap[max] {
// 					max = r
// 				}
// 				if l := 2*i + 2; l < len(heap) && heap[l] > heap[max] {
// 					max = l
// 				}
// 				if max == i {
// 					break
// 				}
//
// 				heap[max], heap[i] = heap[i], heap[max]
// 				i = max
// 			}
// 		}
// 	}
//
// 	j := 1
// 	prev := data[0]
// 	for _, v := range data[1:] {
// 		if v == prev {
// 			continue
// 		}
//
// 		data[j] = v
// 		prev = v
// 	}
//
// 	return data[:j]
// }
//
// func stdSort(data []uint64) []uint64 {
// 	sort.Slice(data, func(i, j int) bool { return data[i] < data[j] })
//
// 	j := 1
// 	prev := data[0]
// 	for _, v := range data[1:] {
// 		if v == prev {
// 			continue
// 		}
//
// 		data[j] = v
// 		prev = v
// 	}
//
// 	return data[:j]
// }
