Files
screwed/sorting/streams.go
T
2015-03-09 20:58:37 -07:00

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
}