pax_global_header00006660000000000000000000000064140412751630014515gustar00rootroot0000000000000052 comment=3860c0f1ff091370f525b207defe8c5d8a1be7ff go-topsort-0.1.1/000077500000000000000000000000001404127516300136315ustar00rootroot00000000000000go-topsort-0.1.1/LICENSE000066400000000000000000000020751404127516300146420ustar00rootroot00000000000000Copyright (c) Paul R. Tagliamonte , 2015 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. go-topsort-0.1.1/README.md000066400000000000000000000015531404127516300151140ustar00rootroot00000000000000topsort ======= This package provides a handy interface to do a topological sort of some data in a pretty lightweight way. Example ------- ```go package main import ( "fmt" "pault.ag/go/topsort" ) func main() { network := topsort.NewNetwork() network.AddNode("watch tv while eating", nil) network.AddNode("make dinner", nil) network.AddNode("clean my kitchen", nil) /* Right, so the order of operations is next */ network.AddEdge("clean my kitchen", "make dinner") // I need to clean the kitchen before I make dinner. network.AddEdge("make dinner", "watch tv while eating") // Need to make dinner before I can eat it. nodes, err := network.Sort() if err != nil { panic(err) } for _, step := range nodes { fmt.Printf(" -> %s\n", step.Name) } /* Output is: * * -> clean my kitchen * -> make dinner * -> watch tv while eating */ } ``` go-topsort-0.1.1/go.mod000066400000000000000000000000441404127516300147350ustar00rootroot00000000000000module pault.ag/go/topsort go 1.16 go-topsort-0.1.1/topsort.go000066400000000000000000000075331404127516300157020ustar00rootroot00000000000000/* {{{ Copyright (c) Paul R. Tagliamonte , 2015 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. }}} */ package topsort import ( "errors" ) // Network Helpers {{{ type Network struct { nodes map[string]*Node order []string } func NewNetwork() *Network { return &Network{ nodes: map[string]*Node{}, order: []string{}, } } func (tn *Network) Sort() ([]*Node, error) { nodes := make([]*Node, 0) for _, key := range tn.order { nodes = append(nodes, tn.nodes[key]) } return sortNodes(nodes) } func (tn *Network) Get(name string) *Node { node, _ := tn.nodes[name] return node } func (tn *Network) AddNode(name string, value interface{}) *Node { node := Node{ Name: name, Value: value, InboundEdges: make([]*Node, 0), OutboundEdges: make([]*Node, 0), Marked: false, } if _, ok := tn.nodes[name]; !ok { tn.order = append(tn.order, name) } tn.nodes[name] = &node return &node } // }}} // Node Helpers {{{ type Node struct { Name string Value interface{} OutboundEdges []*Node InboundEdges []*Node Marked bool } func (node *Node) IsCanidate() bool { for _, edge := range node.InboundEdges { /* for each node, let's check if they're all marked */ if !edge.Marked { return false } } return true } func (tn *Network) AddEdge(from string, to string) error { fromNode := tn.Get(from) toNode := tn.Get(to) if fromNode == nil || toNode == nil { return errors.New("Either the root or target node doesn't exist") } toNode.InboundEdges = append(toNode.InboundEdges, fromNode) fromNode.OutboundEdges = append(fromNode.OutboundEdges, toNode) return nil } func (tn *Network) AddEdgeAndNodes(from string, to string) error { fromNode := tn.Get(from) if fromNode == nil { fromNode = tn.AddNode(from, nil) } toNode := tn.Get(to) if toNode == nil { toNode = tn.AddNode(to, nil) } return tn.AddEdge(from, to) } // }}} // Sort Helpers {{{ func sortSingleNodes(nodes []*Node) ([]*Node, error) { ret := make([]*Node, 0) hasUnprunedNodes := false for _, node := range nodes { if node.Marked { continue /* Already output. */ } hasUnprunedNodes = true /* Otherwise, let's see if we can prune it */ if node.IsCanidate() { /* So, it has no deps and hasn't been marked; let's mark and * output */ node.Marked = true ret = append(ret, node) } } if hasUnprunedNodes && len(ret) == 0 { return nil, errors.New("Cycle detected :(") } return ret, nil } func sortNodes(nodes []*Node) (ret []*Node, err error) { /* Reset Marked status of nodes so they're ready to sort */ for _, node := range nodes { node.Marked = false } for { generation, err := sortSingleNodes(nodes) if err != nil { return nil, err } if len(generation) == 0 { break } ret = append(ret, generation...) } return } // }}} // vim: foldmethod=marker go-topsort-0.1.1/topsort_test.go000066400000000000000000000110721404127516300167320ustar00rootroot00000000000000/* {{{ Copyright (c) Paul R. Tagliamonte , 2015 * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. }}} */ package topsort_test import ( "log" "testing" "pault.ag/go/topsort" ) // Test Helpers {{{ func isok(t *testing.T, err error) { if err != nil { log.Printf("Error! Error is not nil! %s\n", err) t.FailNow() } } func notok(t *testing.T, err error) { if err == nil { log.Printf("Error! Error is nil!\n") t.FailNow() } } func assert(t *testing.T, expr bool) { if !expr { log.Printf("Assertion failed!") t.FailNow() } } func newAlphaNetwork(start, end rune) *topsort.Network { network := topsort.NewNetwork() for c := start; c <= end; c++ { network.AddNode(string(c), nil) } return network } // }}} func TestTopsortEasy(t *testing.T) { network := topsort.NewNetwork() network.AddNode("foo", nil) network.AddNode("bar", nil) network.AddEdge("foo", "bar") series, err := network.Sort() isok(t, err) assert(t, len(series) == 2) } func TestTopsortCycle(t *testing.T) { network := topsort.NewNetwork() network.AddNode("foo", nil) network.AddNode("bar", nil) network.AddEdge("foo", "bar") network.AddEdge("bar", "foo") _, err := network.Sort() notok(t, err) } func TestTopsortLongCycle(t *testing.T) { network := newAlphaNetwork('A', 'I') network.AddEdge("A", "B") network.AddEdge("B", "C") network.AddEdge("C", "D") network.AddEdge("D", "E") network.AddEdge("E", "F") network.AddEdge("F", "G") network.AddEdge("G", "H") network.AddEdge("H", "I") network.AddEdge("I", "A") _, err := network.Sort() notok(t, err) } func TestTopsortLong(t *testing.T) { /* A --> B -> C -> D -> E \ ^ \-> F */ network := newAlphaNetwork('A', 'F') network.AddEdge("A", "B") network.AddEdge("A", "F") network.AddEdge("F", "B") network.AddEdge("B", "C") network.AddEdge("C", "D") network.AddEdge("D", "E") series, err := network.Sort() isok(t, err) assert(t, len(series) == 6) assert(t, series[0].Name == "A") assert(t, series[1].Name == "F") assert(t, series[2].Name == "B") assert(t, series[3].Name == "C") } func TestDeterminism(t *testing.T) { network := newAlphaNetwork('A', 'D') series, err := network.Sort() isok(t, err) assert(t, len(series) == 4) assert(t, series[0].Name == "A") assert(t, series[1].Name == "B") assert(t, series[2].Name == "C") assert(t, series[3].Name == "D") network = newAlphaNetwork('A', 'E') network.AddEdge("D", "A") series, err = network.Sort() isok(t, err) assert(t, len(series) == 5) assert(t, series[0].Name == "B") assert(t, series[1].Name == "C") assert(t, series[2].Name == "D") assert(t, series[3].Name == "E") assert(t, series[4].Name == "A") } func TestReSort(t *testing.T) { network := newAlphaNetwork('A', 'C') series, err := network.Sort() isok(t, err) assert(t, len(series) == 3) assert(t, series[0].Name == "A") assert(t, series[1].Name == "B") assert(t, series[2].Name == "C") network.AddEdge("C", "A") series, err = network.Sort() isok(t, err) assert(t, len(series) == 3) assert(t, series[0].Name == "B") assert(t, series[1].Name == "C") assert(t, series[2].Name == "A") network.AddNode("D", nil) network.AddEdge("D", "B") series, err = network.Sort() isok(t, err) assert(t, len(series) == 4) assert(t, series[0].Name == "C") assert(t, series[1].Name == "D") assert(t, series[2].Name == "A") assert(t, series[3].Name == "B") } func TestTopsortEasyEdges(t *testing.T) { network := topsort.NewNetwork() network.AddEdgeAndNodes("foo", "bar") series, err := network.Sort() isok(t, err) assert(t, len(series) == 2) } // vim: foldmethod=marker