github.com/gopherd/gonum@v0.0.4/graph/graphs/gen/gen.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 gen 6 7 import ( 8 "fmt" 9 10 "github.com/gopherd/gonum/graph" 11 ) 12 13 // GraphBuilder is a graph that can have nodes and edges added. 14 type GraphBuilder interface { 15 HasEdgeBetween(xid, yid int64) bool 16 graph.Builder 17 } 18 19 func abs(a int) int { 20 if a < 0 { 21 return -a 22 } 23 return a 24 } 25 26 // NodeIDGraphBuild is a graph that can create new nodes with 27 // specified IDs. 28 type NodeIDGraphBuilder interface { 29 graph.Builder 30 graph.NodeWithIDer 31 } 32 33 // IDer is a mapping from an index to a node ID. 34 type IDer interface { 35 // Len returns the length of the set of node IDs. 36 Len() int 37 38 // ID returns the ID of the indexed node. 39 // ID must be a bijective function. No check 40 // is made for this property. 41 ID(int) int64 42 } 43 44 // IDRange is an IDer that provides a set of IDs in [First, Last]. 45 type IDRange struct{ First, Last int64 } 46 47 func (r IDRange) Len() int { return int(r.Last - r.First + 1) } 48 func (r IDRange) ID(i int) int64 { return r.First + int64(i) } 49 50 // IDSet is an IDer providing an explicit set of IDs. 51 type IDSet []int64 52 53 func (s IDSet) Len() int { return len(s) } 54 func (s IDSet) ID(i int) int64 { return s[i] } 55 56 // Complete constructs a complete graph in dst using nodes with the given IDs. 57 // If any ID appears twice in ids, Complete will panic. 58 func Complete(dst NodeIDGraphBuilder, ids IDer) { 59 switch ids.Len() { 60 case 0: 61 return 62 case 1: 63 u, new := dst.NodeWithID(ids.ID(0)) 64 if new { 65 dst.AddNode(u) 66 } 67 return 68 } 69 for i := 0; i < ids.Len(); i++ { 70 uid := ids.ID(i) 71 u, _ := dst.NodeWithID(uid) 72 for j := i + 1; j < ids.Len(); j++ { 73 vid := ids.ID(j) 74 if uid == vid { 75 panic(fmt.Errorf("gen: node ID collision i=%d j=%d: id=%d", i, j, uid)) 76 } 77 v, _ := dst.NodeWithID(vid) 78 dst.SetEdge(dst.NewEdge(u, v)) 79 } 80 } 81 } 82 83 // Cycle constructs a cycle in dst using the node IDs in cycle. 84 // If dst is a directed graph, edges are directed from earlier nodes to later 85 // nodes in cycle. If any ID appears twice in cycle, Cycle will panic. 86 func Cycle(dst NodeIDGraphBuilder, cycle IDer) { 87 switch cycle.Len() { 88 case 0: 89 return 90 case 1: 91 u, new := dst.NodeWithID(cycle.ID(0)) 92 if new { 93 dst.AddNode(u) 94 } 95 return 96 } 97 err := check(cycle) 98 if err != nil { 99 panic(err) 100 } 101 cycleNoCheck(dst, cycle) 102 } 103 104 func cycleNoCheck(dst NodeIDGraphBuilder, cycle IDer) { 105 for i := 0; i < cycle.Len(); i++ { 106 uid := cycle.ID(i) 107 vid := cycle.ID((i + 1) % cycle.Len()) 108 u, _ := dst.NodeWithID(uid) 109 v, _ := dst.NodeWithID(vid) 110 dst.SetEdge(dst.NewEdge(u, v)) 111 } 112 } 113 114 // Path constructs a path graph in dst with 115 // If dst is a directed graph, edges are directed from earlier nodes to later 116 // nodes in path. If any ID appears twice in path, Path will panic. 117 func Path(dst NodeIDGraphBuilder, path IDer) { 118 switch path.Len() { 119 case 0: 120 return 121 case 1: 122 u, new := dst.NodeWithID(path.ID(0)) 123 if new { 124 dst.AddNode(u) 125 } 126 return 127 } 128 err := check(path) 129 if err != nil { 130 panic(err) 131 } 132 for i := 0; i < path.Len()-1; i++ { 133 uid := path.ID(i) 134 vid := path.ID(i + 1) 135 u, _ := dst.NodeWithID(uid) 136 v, _ := dst.NodeWithID(vid) 137 dst.SetEdge(dst.NewEdge(u, v)) 138 } 139 } 140 141 // Star constructs a star graph in dst with edges between the center node ID to 142 // node with IDs specified in leaves. 143 // If dst is a directed graph, edges are directed from the center node to the 144 // leaves. If any ID appears twice in leaves and center, Star will panic. 145 func Star(dst NodeIDGraphBuilder, center int64, leaves IDer) { 146 c, new := dst.NodeWithID(center) 147 if new { 148 dst.AddNode(c) 149 } 150 if leaves.Len() == 0 { 151 return 152 } 153 err := check(leaves, center) 154 if err != nil { 155 panic(err) 156 } 157 for i := 0; i < leaves.Len(); i++ { 158 id := leaves.ID(i) 159 n, _ := dst.NodeWithID(id) 160 dst.SetEdge(dst.NewEdge(c, n)) 161 } 162 } 163 164 // Wheel constructs a wheel graph in dst with edges from the center 165 // node ID to node with IDs specified in cycle and between nodes with IDs 166 // adjacent in the cycle. 167 // If dst is a directed graph, edges are directed from the center node to the 168 // cycle and from earlier nodes to later nodes in cycle. If any ID appears 169 // twice in cycle and center, Wheel will panic. 170 func Wheel(dst NodeIDGraphBuilder, center int64, cycle IDer) { 171 Star(dst, center, cycle) 172 if cycle.Len() <= 1 { 173 return 174 } 175 cycleNoCheck(dst, cycle) 176 } 177 178 // Tree constructs an n-ary tree breadth-first in dst with the given fan-out, n. 179 // If the number of nodes does not equal \sum_{i=0}^h n^i, where h is an integer 180 // indicating the height of the tree, a partial tree will be constructed with not 181 // all nodes having zero or n children, and not all leaves being h from the root. 182 // If the number of nodes is greater than one, n must be non-zero and 183 // less than the number of nodes, otherwise Tree will panic. 184 // If dst is a directed graph, edges are directed from the root to the leaves. 185 // If any ID appears more than once in nodes, Tree will panic. 186 func Tree(dst NodeIDGraphBuilder, n int, nodes IDer) { 187 switch nodes.Len() { 188 case 0: 189 return 190 case 1: 191 if u, new := dst.NodeWithID(nodes.ID(0)); new { 192 dst.AddNode(u) 193 } 194 return 195 } 196 197 if n < 1 || nodes.Len() <= n { 198 panic("gen: invalid fan-out") 199 } 200 201 err := check(nodes) 202 if err != nil { 203 panic(err) 204 } 205 206 j := 0 207 for i := 0; j < nodes.Len(); i++ { 208 u, _ := dst.NodeWithID(nodes.ID(i)) 209 for j = n*i + 1; j <= n*i+n && j < nodes.Len(); j++ { 210 v, _ := dst.NodeWithID(nodes.ID(j)) 211 dst.SetEdge(dst.NewEdge(u, v)) 212 } 213 } 214 } 215 216 // check confirms that no node ID exists more than once in ids and extra. 217 func check(ids IDer, extra ...int64) error { 218 seen := make(map[int64]int, ids.Len()+len(extra)) 219 for j := 0; j < ids.Len(); j++ { 220 uid := ids.ID(j) 221 if prev, exists := seen[uid]; exists { 222 return fmt.Errorf("gen: node ID collision i=%d j=%d: id=%d", prev, j, uid) 223 } 224 seen[uid] = j 225 } 226 lenIDs := ids.Len() 227 for j, uid := range extra { 228 if prev, exists := seen[uid]; exists { 229 if prev < lenIDs { 230 if len(extra) == 1 { 231 return fmt.Errorf("gen: node ID collision i=%d with extra: id=%d", prev, uid) 232 } 233 return fmt.Errorf("gen: node ID collision i=%d with extra j=%d: id=%d", prev, j, uid) 234 } 235 prev -= lenIDs 236 return fmt.Errorf("gen: extra node ID collision i=%d j=%d: id=%d", prev, j, uid) 237 } 238 seen[uid] = j + lenIDs 239 } 240 return nil 241 }