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  }