github.com/gopherd/gonum@v0.0.4/graph/encoding/graph6/graph6.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 graph6 implements graphs specified by graph6 strings. 6 package graph6 // import "github.com/gopherd/gonum/graph/encoding/graph6" 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 graph6-represented undirected graph. 20 // 21 // See https://users.cecs.anu.edu.au/~bdm/data/formats.txt for details 22 // and https://hog.grinvin.org/ for a source of interesting graphs in graph6 23 // format. 24 type Graph string 25 26 var ( 27 g6 Graph 28 29 _ graph.Graph = g6 30 _ graph.Undirected = g6 31 ) 32 33 // Encode returns a graph6 encoding of the topology of the given graph using a 34 // lexical ordering of the nodes by ID to map them to [0, n). 35 func Encode(g graph.Graph) Graph { 36 nodes := graph.NodesOf(g.Nodes()) 37 n := len(nodes) 38 ordered.ByID(nodes) 39 indexOf := make(map[int64]int, n) 40 for i, n := range nodes { 41 indexOf[n.ID()] = i 42 } 43 44 size := (n*n - n) / 2 45 var b big.Int 46 for i, u := range nodes { 47 uid := u.ID() 48 it := g.From(uid) 49 for it.Next() { 50 vid := it.Node().ID() 51 if vid < uid { 52 continue 53 } 54 j := indexOf[vid] 55 b.SetBit(&b, bitFor(int64(i), int64(j)), 1) 56 } 57 } 58 59 var buf strings.Builder 60 // graph6 specifies graphs of order up to 2^36-1 which 61 // overflows int on 32-bit architectures. We know that on 62 // those machines n will not be this large, since it came 63 // from a length, but explicitly convert to 64 bits to 64 // allow the package to build on those architectures. 65 // 66 // See the section Small nonnegative integers in the spec 67 // for details of this section. 68 switch n := int64(n); { 69 case n < 63: 70 buf.WriteByte(byte(n) + 63) 71 case n < 258048: 72 buf.Write([]byte{126, bit6(n>>12) + 63, bit6(n>>6) + 63, bit6(n) + 63}) 73 case n < 68719476736: 74 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}) 75 default: 76 panic("graph6: too large") 77 } 78 79 var c byte 80 for i := 0; i < size; i++ { 81 bit := i % 6 82 c |= byte(b.Bit(i)) << uint(5-bit) 83 if bit == 5 { 84 buf.WriteByte(c + 63) 85 c = 0 86 } 87 } 88 if size%6 != 0 { 89 buf.WriteByte(c + 63) 90 } 91 92 return Graph(buf.String()) 93 } 94 95 // bit6 returns only the lower 6 bits of b. 96 func bit6(b int64) byte { 97 return byte(b) & 0x3f 98 } 99 100 // IsValid returns whether the graph is a valid graph6 encoding. An invalid Graph 101 // behaves as the null graph. 102 func IsValid(g Graph) bool { 103 n := int(numberOf(g)) 104 if n < 0 { 105 return false 106 } 107 size := ((n*n-n)/2 + 5) / 6 // ceil(((n*n-n)/2) / 6) 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.HasEdgeBetween(uid, vid) { 126 return nil 127 } 128 return simple.Edge{F: simple.Node(uid), T: simple.Node(vid)} 129 } 130 131 // EdgeBetween returns the edge between nodes x and y with IDs xid and yid. 132 func (g Graph) EdgeBetween(xid, yid int64) graph.Edge { 133 return g.Edge(xid, yid) 134 } 135 136 // From returns all nodes that can be reached directly from the node with the 137 // given ID. 138 func (g Graph) From(id int64) graph.Nodes { 139 if !IsValid(g) { 140 return graph.Empty 141 } 142 if g.Node(id) == nil { 143 return nil 144 } 145 return &g6Iterator{g: g, from: id, to: -1} 146 } 147 148 // HasEdgeBetween returns whether an edge exists between nodes with IDs xid 149 // and yid without considering direction. 150 func (g Graph) HasEdgeBetween(xid, yid int64) bool { 151 if !IsValid(g) { 152 return false 153 } 154 if xid == yid { 155 return false 156 } 157 if xid < 0 || numberOf(g) <= xid { 158 return false 159 } 160 if yid < 0 || numberOf(g) <= yid { 161 return false 162 } 163 return isSet(bitFor(xid, yid), g) 164 } 165 166 // Node returns the node with the given ID if it exists in the graph, and nil 167 // otherwise. 168 func (g Graph) Node(id int64) graph.Node { 169 if !IsValid(g) { 170 return nil 171 } 172 if id < 0 || numberOf(g) <= id { 173 return nil 174 } 175 return simple.Node(id) 176 } 177 178 // Nodes returns all the nodes in the graph. 179 func (g Graph) Nodes() graph.Nodes { 180 if !IsValid(g) { 181 return graph.Empty 182 } 183 return iterator.NewImplicitNodes(0, int(numberOf(g)), func(id int) graph.Node { return simple.Node(id) }) 184 } 185 186 // g6Iterator is a graph.Nodes for graph6 graph edges. 187 type g6Iterator struct { 188 g Graph 189 from int64 190 to int64 191 } 192 193 var _ graph.Nodes = (*g6Iterator)(nil) 194 195 func (i *g6Iterator) Next() bool { 196 n := numberOf(i.g) 197 for i.to < n-1 { 198 i.to++ 199 if i.to != i.from && isSet(bitFor(i.from, i.to), i.g) { 200 return true 201 } 202 } 203 return false 204 } 205 206 func (i *g6Iterator) Len() int { 207 var cnt int 208 n := numberOf(i.g) 209 for to := i.to; to < n-1; { 210 to++ 211 if to != i.from && isSet(bitFor(i.from, to), i.g) { 212 cnt++ 213 } 214 } 215 return cnt 216 } 217 218 func (i *g6Iterator) Reset() { i.to = -1 } 219 220 func (i *g6Iterator) Node() graph.Node { return simple.Node(i.to) } 221 222 // numberOf returns the graph6-encoded number corresponding to g. 223 func numberOf(g Graph) int64 { 224 if len(g) < 1 { 225 return -1 226 } 227 for _, b := range []byte(g) { 228 if b < 63 || 126 < b { 229 return -1 230 } 231 } 232 if g[0] != 126 { 233 return int64(g[0] - 63) 234 } 235 if len(g) < 4 { 236 return -1 237 } 238 if g[1] != 126 { 239 return int64(g[1]-63)<<12 | int64(g[2]-63)<<6 | int64(g[3]-63) 240 } 241 if len(g) < 8 { 242 return -1 243 } 244 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) 245 } 246 247 // bitFor returns the index into the graph6 adjacency matrix for xid--yid. 248 func bitFor(xid, yid int64) int { 249 if xid < yid { 250 xid, yid = yid, xid 251 } 252 return int((xid*xid-xid)/2 + yid) 253 } 254 255 // isSet returns whether the given bit of the adjacency matrix is set. 256 func isSet(bit int, g Graph) bool { 257 switch { 258 case g[0] != 126: 259 g = g[1:] 260 case g[1] != 126: 261 g = g[4:] 262 default: 263 g = g[8:] 264 } 265 if bit/6 >= len(g) { 266 panic("g6: index out of range") 267 } 268 return (g[bit/6]-63)&(1<<uint(5-bit%6)) != 0 269 } 270 271 func (g Graph) GoString() string { 272 bin, m6 := binary(g) 273 format := fmt.Sprintf("%%d:%%0%db", m6) 274 return fmt.Sprintf(format, numberOf(g), bin) 275 } 276 277 func binary(g Graph) (b *big.Int, l int) { 278 n := int(numberOf(g)) 279 280 switch { 281 case g[0] != 126: 282 g = g[1:] 283 case g[1] != 126: 284 g = g[4:] 285 default: 286 g = g[8:] 287 } 288 b = &big.Int{} 289 var c big.Int 290 for i := range g { 291 c.SetUint64(uint64(g[len(g)-i-1] - 63)) 292 c.Lsh(&c, uint(6*i)) 293 b.Or(b, &c) 294 } 295 296 // Truncate to only the relevant parts of the bit vector. 297 b.Rsh(b, uint(len(g)*6-(n*n-n)/2)) 298 299 return b, (n*n - n) / 2 300 }