well-goknown/vendor/fiatjaf.com/lib/merge-sorted-slices/merge.go

156 lines
4 KiB
Go
Raw Normal View History

2024-11-29 19:44:33 +00:00
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
}