github.com/jingcheng-WU/gonum@v0.9.1-0.20210323123734-f1a2a11a8f7b/graph/topo/paton_cycles_test.go (about) 1 // Copyright ©2015 The Gonum Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style 3 // license that can be found in the LICENSE file. 4 5 package topo 6 7 import ( 8 "reflect" 9 "sort" 10 "testing" 11 12 "github.com/jingcheng-WU/gonum/graph" 13 "github.com/jingcheng-WU/gonum/graph/internal/ordered" 14 "github.com/jingcheng-WU/gonum/graph/simple" 15 ) 16 17 var undirectedCyclesInTests = []struct { 18 g []intset 19 want [][][]int64 20 }{ 21 { 22 g: []intset{ 23 0: linksTo(1, 2), 24 1: linksTo(2, 4, 5, 9), 25 2: linksTo(4, 7, 9), 26 3: linksTo(5), 27 4: linksTo(8), 28 5: linksTo(7, 8), 29 6: nil, 30 7: nil, 31 8: nil, 32 9: nil, 33 10: linksTo(11, 12), 34 11: linksTo(12), 35 12: nil, 36 }, 37 want: [][][]int64{ 38 { 39 {0, 1, 2, 0}, 40 {1, 2, 7, 5, 1}, 41 {1, 2, 9, 1}, 42 {1, 4, 8, 5, 1}, 43 {2, 4, 8, 5, 7, 2}, 44 {10, 11, 12, 10}, 45 }, 46 { 47 {0, 1, 2, 0}, 48 {1, 2, 4, 1}, 49 {1, 2, 7, 5, 1}, 50 {1, 2, 9, 1}, 51 {1, 4, 8, 5, 1}, 52 {10, 11, 12, 10}, 53 }, 54 { 55 {0, 1, 2, 0}, 56 {1, 2, 4, 1}, 57 {1, 2, 9, 1}, 58 {1, 4, 8, 5, 1}, 59 {2, 4, 8, 5, 7, 2}, 60 {10, 11, 12, 10}, 61 }, 62 { 63 {0, 1, 2, 0}, 64 {1, 2, 4, 1}, 65 {1, 2, 7, 5, 1}, 66 {1, 2, 9, 1}, 67 {2, 4, 8, 5, 7, 2}, 68 {10, 11, 12, 10}, 69 }, 70 }, 71 }, 72 } 73 74 func TestUndirectedCyclesIn(t *testing.T) { 75 for i, test := range undirectedCyclesInTests { 76 g := simple.NewUndirectedGraph() 77 g.AddNode(simple.Node(-10)) // Make sure we test graphs with sparse IDs. 78 for u, e := range test.g { 79 // Add nodes that are not defined by an edge. 80 if g.Node(int64(u)) == nil { 81 g.AddNode(simple.Node(u)) 82 } 83 for v := range e { 84 g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)}) 85 } 86 } 87 cycles := UndirectedCyclesIn(g) 88 var got [][]int64 89 if cycles != nil { 90 got = make([][]int64, len(cycles)) 91 } 92 // Canonicalise the cycles. 93 for j, c := range cycles { 94 ids := make([]int64, len(c)) 95 for k, n := range canonicalise(c[:len(c)-1]) { 96 ids[k] = n.ID() 97 } 98 ids[len(ids)-1] = ids[0] 99 got[j] = ids 100 } 101 sort.Sort(ordered.BySliceValues(got)) 102 var matched bool 103 for _, want := range test.want { 104 if reflect.DeepEqual(got, want) { 105 matched = true 106 break 107 } 108 } 109 if !matched { 110 t.Errorf("unexpected paton result for %d:\n\tgot:%#v\n\twant from:%#v", i, got, test.want) 111 } 112 } 113 } 114 115 // canonicalise returns the cycle path c cyclicly permuted such that 116 // the first element has the lowest ID and then conditionally 117 // reversed so that the second element has the lowest possible 118 // neighbouring ID. 119 // c lists each node only onces - the final node must not be a 120 // reiteration of the first node. 121 func canonicalise(c []graph.Node) []graph.Node { 122 if len(c) < 2 { 123 return c 124 } 125 idx := 0 126 min := c[0].ID() 127 for i, n := range c[1:] { 128 if id := n.ID(); id < min { 129 idx = i + 1 130 min = id 131 } 132 } 133 if idx != 0 { 134 c = append(c[idx:], c[:idx]...) 135 } 136 if c[len(c)-1].ID() < c[1].ID() { 137 ordered.Reverse(c[1:]) 138 } 139 return c 140 }