134 lines
3.0 KiB
Go
134 lines
3.0 KiB
Go
package sorting
|
|
|
|
import "fmt"
|
|
|
|
var _ = fmt.Print
|
|
|
|
type IsGreaterer interface {
|
|
// return true iff "this" is strictly greater than e
|
|
IsGreater(e IsGreaterer) bool
|
|
}
|
|
|
|
type alwaysBigger struct{}
|
|
|
|
func (a *alwaysBigger) IsGreater(e IsGreaterer) bool {
|
|
return e != nil
|
|
}
|
|
|
|
type Stream chan IsGreaterer
|
|
|
|
type streamOutput struct {
|
|
Val IsGreaterer
|
|
Idx int
|
|
}
|
|
|
|
func readFirstElementOfAllTheStreams(streams []Stream) []IsGreaterer {
|
|
values := make([]IsGreaterer, len(streams))
|
|
ch := make(chan *streamOutput)
|
|
for i, _ := range values {
|
|
go func(idx int) {
|
|
v := <-streams[idx]
|
|
ch <- &streamOutput{v, idx}
|
|
|
|
}(i)
|
|
}
|
|
|
|
i := 0
|
|
for streamOutput := range ch {
|
|
values[streamOutput.Idx] = streamOutput.Val
|
|
i++
|
|
if i == len(streams) {
|
|
return values
|
|
}
|
|
}
|
|
return values
|
|
}
|
|
|
|
func allStreamsButOne(streams []Stream, idx int) []Stream {
|
|
ret := make([]Stream, len(streams)-1)
|
|
for i, s := range streams {
|
|
if i < idx {
|
|
ret[i] = s
|
|
} else if i > idx {
|
|
ret[i-1] = s
|
|
}
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func prependToStream(streams Stream, toPrepend IsGreaterer) Stream {
|
|
ret := make(Stream)
|
|
go func() {
|
|
ret <- toPrepend
|
|
for e := range streams {
|
|
ret <- e
|
|
}
|
|
close(ret)
|
|
}()
|
|
return ret
|
|
}
|
|
|
|
func prependValueToEachStream(streams []Stream, values []IsGreaterer) []Stream {
|
|
ret := make([]Stream, len(streams))
|
|
for i := 0; i < len(streams); i++ {
|
|
ret[i] = prependToStream(streams[i], values[i])
|
|
|
|
}
|
|
return ret
|
|
}
|
|
|
|
func getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(streams []Stream) (Stream, IsGreaterer, []Stream) {
|
|
firstElements := readFirstElementOfAllTheStreams(streams)
|
|
streams = prependValueToEachStream(streams, firstElements)
|
|
|
|
var smallestStreamIndex int = -1
|
|
var smallestItem IsGreaterer = &alwaysBigger{}
|
|
var secondSmallestItem IsGreaterer = &alwaysBigger{}
|
|
|
|
for i, e := range firstElements {
|
|
if smallestItem.IsGreater(e) {
|
|
smallestItem = e
|
|
smallestStreamIndex = i
|
|
} else if secondSmallestItem.IsGreater(e) {
|
|
secondSmallestItem = e
|
|
}
|
|
}
|
|
if _, ok := secondSmallestItem.(*alwaysBigger); ok {
|
|
return streams[smallestStreamIndex], nil, []Stream{}
|
|
}
|
|
return streams[smallestStreamIndex], secondSmallestItem, allStreamsButOne(streams, smallestStreamIndex)
|
|
}
|
|
|
|
func sortStreamInChannel(o Stream, streams []Stream) {
|
|
if len(streams) == 1 {
|
|
for e := range streams[0] {
|
|
o <- e
|
|
}
|
|
close(o)
|
|
return
|
|
}
|
|
smallestStream, secondSmallestItem, greaterStreams := getSmallerStreamWithSecondSmallestElementWithAllTheOthersGreaterStreams(streams)
|
|
if secondSmallestItem == nil {
|
|
sortStreamInChannel(o, append(greaterStreams, smallestStream))
|
|
return
|
|
|
|
}
|
|
for elm := range smallestStream {
|
|
if !elm.IsGreater(secondSmallestItem) {
|
|
o <- elm
|
|
} else {
|
|
smallestStream = prependToStream(smallestStream, elm)
|
|
sortStreamInChannel(o, append(greaterStreams, smallestStream))
|
|
return
|
|
}
|
|
}
|
|
sortStreamInChannel(o, greaterStreams)
|
|
}
|
|
|
|
// Takes sorted streams as input and return the merged result stream
|
|
func SortStreams(streams ...Stream) Stream {
|
|
ret := make(Stream)
|
|
go sortStreamInChannel(ret, streams)
|
|
return ret
|
|
}
|