package sorting import ( "sync" ) func merge[T any](items []T, low, mid, hig int, less func(a, b T) bool, aux []T) { i, j := low, mid+1 for k := low; k <= hig; k++ { aux[k] = items[k] } for k := low; k <= hig; k++ { if i > mid { items[k] = aux[j] j++ } else if j > hig { items[k] = aux[i] i++ } else if less(aux[i], aux[j]) { items[k] = aux[i] i++ } else { items[k] = aux[j] j++ } } } func doMergeSort[T any](items []T, low, hig int, less func(a, b T) bool, aux []T) { if hig <= low { return } // Optimization 2: if array length is less than 15 // it's imperically prooven that insertion sort // for such arrays can speed up merge sort up to 15% if hig-low <= 15 { Insertion(items[low:hig+1], less) return } mid := low + (hig-low)/2 doMergeSort(items, low, mid, less, aux) doMergeSort(items, mid+1, hig, less, aux) // Optimization 1: if two subarrays already sorted // we can skip merging them if less(items[mid], items[mid+1]) { return } merge(items, low, mid, hig, less, aux) } func doParallelSort[T any](items []T, low, hig int, less func(a, b T) bool, aux []T) { if hig <= low { return } if hig-low <= 15 { Insertion(items[low:hig+1], less) return } mid := low + (hig-low)/2 // Optimization 3: we can run sorting in parallel. // This is costly optimization because we increase memory // usage by creating WaitGroup instances wg := sync.WaitGroup{} wg.Add(2) go func() { doParallelSort(items, low, mid, less, aux); wg.Done() }() go func() { doParallelSort(items, mid+1, hig, less, aux); wg.Done() }() wg.Wait() if less(items[mid], items[mid+1]) { return } merge(items, low, mid, hig, less, aux) } func Merge[T any](items []T, less func(a, b T) bool) { len := len(items) doMergeSort(items, 0, len-1, less, make([]T, len)) } func ParallelMerge[T any](items []T, less func(a, b T) bool) { len := len(items) doParallelSort(items, 0, len-1, less, make([]T, len)) } func BottomUpMerge[T any](items []T, less func(a, b T) bool) { len := len(items) aux := make([]T, len) min := func(x, y int) int { if x < y { return x } return y } for sz := 1; sz < len; sz = sz + sz { for low := 0; low < len-sz; low += sz + sz { merge(items, low, low+sz-1, min(low+sz+sz-1, len-1), less, aux) } } }