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