package mergesortedslices import ( "cmp" ) // Merge take a bunch of simple lists and merges them. They must be ordered otherwise everything will be broken. // Merge will order stuff with bigger values first, which is the opposite of what everybody else does. // Why? Because that how I needed it to work. It will also expect the given lists to be ordered like that. func Merge[A cmp.Ordered](all [][]A) []A { total := 0 for i := len(all) - 1; i >= 0; i-- { if len(all[i]) == 0 { // remove empty lists all = swapDelete(all, i) } else { // count total total += len(all[i]) } } return MergeLimitNoEmptyLists(all, total) } // MergeLimit is the same as Merge, but takes a limit. While Merge will order all the items in all the lists // MergeLimit will stop when it reaches limit, which means it will be much faster if the limit is smaller than // the total count of all items. func MergeLimit[A cmp.Ordered](all [][]A, limit int) []A { // remove empty lists for i := len(all) - 1; i >= 0; i-- { if len(all[i]) == 0 { all = swapDelete(all, i) } } return MergeLimitNoEmptyLists(all, limit) } // MergeLimitNoEmptyLists is the same as MergeLimit, but assumes there are not empty lists, like MergeNoEmptyLists. func MergeLimitNoEmptyLists[A cmp.Ordered](all [][]A, limit int) []A { return MergeNoEmptyListsIntoSlice(make([]A, limit), all) } // MergeNoEmptyListsIntoSlice is the same as MergeLimitNoEmptyLists, but take a slice into which it will insert the results. The slice length will not be increased, its length will be treated as the max limit. func MergeNoEmptyListsIntoSlice[A cmp.Ordered](res []A, all [][]A) []A { any := make([]int, len(all)) for i := 0; i < len(res); i++ { if len(any) == 0 { res = res[0:i] break } var fst A var fstSrc int = -1 var snd A var sndSrc int = -1 for a, next := range any { if v := all[a][next]; fstSrc == -1 || cmp.Compare(v, fst) == 1 { snd, fst = fst, v sndSrc, fstSrc = fstSrc, a } else if sndSrc == -1 || cmp.Compare(v, snd) == 1 { snd = v sndSrc = a } } // fmt.Println("fst", fst, "from", fstSrc, all[fstSrc]) // if sndSrc == -1 { // fmt.Println(" snd is none") // } else { // fmt.Println(" snd", snd, "from", sndSrc, all[sndSrc]) // } // fmt.Println(" adding", fst) any[fstSrc]++ res[i] = fst if len(all[fstSrc]) == any[fstSrc] { // queue from which the first value came is exhausted, so // fmt.Println(" adding", fst, "(immediate snd)") // remove the first queue any = swapDelete(any, fstSrc) all = swapDelete(all, fstSrc) if sndSrc == len(any) { sndSrc = fstSrc } // add the second value if sndSrc > -1 { i++ if i == len(res) { break } res[i] = snd any[sndSrc]++ if len(all[sndSrc]) == any[sndSrc] { // potentially remove the second queue any = swapDelete(any, sndSrc) all = swapDelete(all, sndSrc) // fmt.Println(" %%", sndSrc) } } } else { // first queue is not exhausted, so try to fetch its next value and compare it with the second for n := all[fstSrc][any[fstSrc]]; cmp.Compare(n, snd) > -1; n = all[fstSrc][any[fstSrc]] { // as long as the first value is greater than the second we keep adding it // fmt.Println(" adding", n, "(fst again)") i++ if i == len(res) { break } res[i] = n any[fstSrc]++ // or until the first queue is exhausted if len(all[fstSrc]) == any[fstSrc] { any = swapDelete(any, fstSrc) all = swapDelete(all, fstSrc) // fmt.Println(" %%", fstSrc) if sndSrc == len(any) { sndSrc = fstSrc } break } } // then we add the second if sndSrc > -1 { // fmt.Println(" adding", snd, "(final snd)") i++ if i >= len(res) { break } res[i] = snd any[sndSrc]++ if len(all[sndSrc]) == any[sndSrc] { any = swapDelete(any, sndSrc) all = swapDelete(all, sndSrc) // fmt.Println(" %%", sndSrc) } } } // fmt.Println(" ::", res) } return res }