github.com/gopherd/gonum@v0.0.4/graph/encoding/digraph6/digraph6.go (about) 1 // Copyright ©2018 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 digraph6 implements graphs specified by digraph6 strings. 6 package digraph6 // import "github.com/gopherd/gonum/graph/encoding/digraph6" 7 8 import ( 9 "fmt" 10 "math/big" 11 "strings" 12 13 "github.com/gopherd/gonum/graph" 14 "github.com/gopherd/gonum/graph/internal/ordered" 15 "github.com/gopherd/gonum/graph/iterator" 16 "github.com/gopherd/gonum/graph/simple" 17 ) 18 19 // Graph is a digraph6-represented directed graph. 20 // 21 // See https://users.cecs.anu.edu.au/~bdm/data/formats.txt for details. 22 // 23 // Note that the digraph6 format specifies that the first character of the graph 24 // string is a '&'. This character must be present for use in the digraph6 package. 25 // A Graph without this prefix is treated as the null graph. 26 type Graph string 27 28 var ( 29 d6 Graph 30 31 _ graph.Graph = d6 32 _ graph.Directed = d6 33 ) 34 35 // Encode returns a graph6 encoding of the topology of the given graph using a 36 // lexical ordering of the nodes by ID to map them to [0, n). 37 func Encode(g graph.Graph) Graph { 38 nodes := graph.NodesOf(g.Nodes()) 39 n := len(nodes) 40 ordered.ByID(nodes) 41 indexOf := make(map[int64]int, n) 42 for i, n := range nodes { 43 indexOf[n.ID()] = i 44 } 45 46 size := n * n 47 var b big.Int 48 for i, u := range nodes { 49 it := g.From(u.ID()) 50 for it.Next() { 51 vid := it.Node().ID() 52 j := indexOf[vid] 53 b.SetBit(&b, bitFor(int64(i), int64(j), int64(n)), 1) 54 } 55 } 56 57 var buf strings.Builder 58 buf.WriteByte('&') 59 // digraph6 specifies graphs of order up to 2^36-1 which 60 // overflows int on 32-bit architectures. We know that on 61 // those machines n will not be this large, since it came 62 // from a length, but explicitly convert to 64 bits to 63 // allow the package to build on those architectures. 64 // 65 // See the section Small nonnegative integers in the spec 66 // for details of this section. 67 switch n := int64(n); { 68 case n < 63: 69 buf.WriteByte(byte(n) + 63) 70 case n < 258048: 71 buf.Write([]byte{126, bit6(n>>12) + 63, bit6(n>>6) + 63, bit6(n) + 63}) 72 case n < 68719476736: 73 buf.Write([]byte{126, 126, bit6(n>>30) + 63, bit6(n>>24) + 63, bit6(n>>18) + 63, bit6(n>>12) + 63, bit6(n>>6) + 63, bit6(n) + 63}) 74 default: 75 panic("digraph6: too large") 76 } 77 78 var c byte 79 for i := 0; i < size; i++ { 80 bit := i % 6 81 c |= byte(b.Bit(i)) << uint(5-bit) 82 if bit == 5 { 83 buf.WriteByte(c + 63) 84 c = 0 85 } 86 } 87 if size%6 != 0 { 88 buf.WriteByte(c + 63) 89 } 90 91 return Graph(buf.String()) 92 } 93 94 // bit6 returns only the lower 6 bits of b. 95 func bit6(b int64) byte { 96 return byte(b) & 0x3f 97 } 98 99 // IsValid returns whether the graph is a valid digraph6 encoding. An invalid Graph 100 // behaves as the null graph. 101 func IsValid(g Graph) bool { 102 n := int(numberOf(g)) 103 if n < 0 { 104 return false 105 } 106 size := (n*n + 5) / 6 // ceil(n^2 / 6) 107 g = g[1:] 108 switch { 109 case g[0] != 126: 110 return len(g[1:]) == size 111 case g[1] != 126: 112 return len(g[4:]) == size 113 default: 114 return len(g[8:]) == size 115 } 116 } 117 118 // Edge returns the edge from u to v, with IDs uid and vid, if such an edge 119 // exists and nil otherwise. The node v must be directly reachable from u as 120 // defined by the From method. 121 func (g Graph) Edge(uid, vid int64) graph.Edge { 122 if !IsValid(g) { 123 return nil 124 } 125 if !g.HasEdgeFromTo(uid, vid) { 126 return nil 127 } 128 return simple.Edge{F: simple.Node(uid), T: simple.Node(vid)} 129 } 130 131 // From returns all nodes that can be reached directly from the node with the 132 // given ID. 133 func (g Graph) From(id int64) graph.Nodes { 134 if !IsValid(g) { 135 return graph.Empty 136 } 137 if g.Node(id) == nil { 138 return nil 139 } 140 return &d6ForwardIterator{g: g, from: id, to: -1} 141 } 142 143 // HasEdgeBetween returns whether an edge exists between nodes with IDs xid 144 // and yid without considering direction. 145 func (g Graph) HasEdgeBetween(xid, yid int64) bool { 146 if !IsValid(g) { 147 return false 148 } 149 return g.HasEdgeFromTo(xid, yid) || g.HasEdgeFromTo(yid, xid) 150 } 151 152 // HasEdgeFromTo returns whether an edge exists in the graph from u to v with 153 // IDs uid and vid. 154 func (g Graph) HasEdgeFromTo(uid, vid int64) bool { 155 if !IsValid(g) { 156 return false 157 } 158 if uid == vid { 159 return false 160 } 161 n := numberOf(g) 162 if uid < 0 || n <= uid { 163 return false 164 } 165 if vid < 0 || n <= vid { 166 return false 167 } 168 return isSet(bitFor(uid, vid, n), g) 169 } 170 171 // Node returns the node with the given ID if it exists in the graph, and nil 172 // otherwise. 173 func (g Graph) Node(id int64) graph.Node { 174 if !IsValid(g) { 175 return nil 176 } 177 if id < 0 || numberOf(g) <= id { 178 return nil 179 } 180 return simple.Node(id) 181 } 182 183 // Nodes returns all the nodes in the graph. 184 func (g Graph) Nodes() graph.Nodes { 185 if !IsValid(g) { 186 return graph.Empty 187 } 188 return iterator.NewImplicitNodes(0, int(numberOf(g)), func(id int) graph.Node { return simple.Node(id) }) 189 } 190 191 // To returns all nodes that can reach directly to the node with the given ID. 192 func (g Graph) To(id int64) graph.Nodes { 193 if !IsValid(g) || g.Node(id) == nil { 194 return graph.Empty 195 } 196 return &d6ReverseIterator{g: g, from: -1, to: id} 197 } 198 199 // d6ForwardIterator is a graph.Nodes for digraph6 graph edges for forward hops. 200 type d6ForwardIterator struct { 201 g Graph 202 from int64 203 to int64 204 } 205 206 var _ graph.Nodes = (*d6ForwardIterator)(nil) 207 208 func (i *d6ForwardIterator) Next() bool { 209 n := numberOf(i.g) 210 for i.to < n-1 { 211 i.to++ 212 if i.to != i.from && isSet(bitFor(i.from, i.to, n), i.g) { 213 return true 214 } 215 } 216 return false 217 } 218 219 func (i *d6ForwardIterator) Len() int { 220 var cnt int 221 n := numberOf(i.g) 222 for to := i.to; to < n-1; { 223 to++ 224 if to != i.from && isSet(bitFor(i.from, to, n), i.g) { 225 cnt++ 226 } 227 } 228 return cnt 229 } 230 231 func (i *d6ForwardIterator) Reset() { i.to = -1 } 232 233 func (i *d6ForwardIterator) Node() graph.Node { return simple.Node(i.to) } 234 235 // d6ReverseIterator is a graph.Nodes for digraph6 graph edges for reverse hops. 236 type d6ReverseIterator struct { 237 g Graph 238 from int64 239 to int64 240 } 241 242 var _ graph.Nodes = (*d6ReverseIterator)(nil) 243 244 func (i *d6ReverseIterator) Next() bool { 245 n := numberOf(i.g) 246 for i.from < n-1 { 247 i.from++ 248 if i.to != i.from && isSet(bitFor(i.from, i.to, n), i.g) { 249 return true 250 } 251 } 252 return false 253 } 254 255 func (i *d6ReverseIterator) Len() int { 256 var cnt int 257 n := numberOf(i.g) 258 for from := i.from; from < n-1; { 259 from++ 260 if from != i.to && isSet(bitFor(from, i.to, n), i.g) { 261 cnt++ 262 } 263 } 264 return cnt 265 } 266 267 func (i *d6ReverseIterator) Reset() { i.from = -1 } 268 269 func (i *d6ReverseIterator) Node() graph.Node { return simple.Node(i.from) } 270 271 // numberOf returns the digraph6-encoded number corresponding to g. 272 func numberOf(g Graph) int64 { 273 if len(g) < 2 { 274 return -1 275 } 276 if g[0] != '&' { 277 return -1 278 } 279 g = g[1:] 280 for _, b := range []byte(g) { 281 if b < 63 || 126 < b { 282 return -1 283 } 284 } 285 if g[0] != 126 { 286 return int64(g[0] - 63) 287 } 288 if len(g) < 4 { 289 return -1 290 } 291 if g[1] != 126 { 292 return int64(g[1]-63)<<12 | int64(g[2]-63)<<6 | int64(g[3]-63) 293 } 294 if len(g) < 8 { 295 return -1 296 } 297 return int64(g[2]-63)<<30 | int64(g[3]-63)<<24 | int64(g[4]-63)<<18 | int64(g[5]-63)<<12 | int64(g[6]-63)<<6 | int64(g[7]-63) 298 } 299 300 // bitFor returns the index into the digraph6 adjacency matrix for uid->vid in a graph 301 // order n. 302 func bitFor(uid, vid, n int64) int { 303 return int(uid*n + vid) 304 } 305 306 // isSet returns whether the given bit of the adjacency matrix is set. 307 func isSet(bit int, g Graph) bool { 308 g = g[1:] 309 switch { 310 case g[0] != 126: 311 g = g[1:] 312 case g[1] != 126: 313 g = g[4:] 314 default: 315 g = g[8:] 316 } 317 if bit/6 >= len(g) { 318 panic("digraph6: index out of range") 319 } 320 return (g[bit/6]-63)&(1<<uint(5-bit%6)) != 0 321 } 322 323 func (g Graph) GoString() string { 324 if !IsValid(g) { 325 return "" 326 } 327 bin, m6 := binary(g) 328 format := fmt.Sprintf("%%d:%%0%db", m6) 329 return fmt.Sprintf(format, numberOf(g), bin) 330 } 331 332 func binary(g Graph) (b *big.Int, l int) { 333 n := int(numberOf(g)) 334 g = g[1:] 335 switch { 336 case g[0] != 126: 337 g = g[1:] 338 case g[1] != 126: 339 g = g[4:] 340 default: 341 g = g[8:] 342 } 343 b = &big.Int{} 344 var c big.Int 345 for i := range g { 346 c.SetUint64(uint64(g[len(g)-i-1] - 63)) 347 c.Lsh(&c, uint(6*i)) 348 b.Or(b, &c) 349 } 350 351 // Truncate to only the relevant parts of the bit vector. 352 b.Rsh(b, uint(len(g)*6-(n*n))) 353 354 return b, n * n 355 }