mirror of
https://github.com/gonum/gonum.git
synced 2025-10-06 23:52:47 +08:00
264 lines
5.9 KiB
Go
264 lines
5.9 KiB
Go
// Copyright ©2018 The Gonum Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
package graph6
|
|
|
|
import (
|
|
"reflect"
|
|
"testing"
|
|
|
|
"gonum.org/v1/gonum/graph"
|
|
"gonum.org/v1/gonum/graph/iterator"
|
|
"gonum.org/v1/gonum/graph/simple"
|
|
)
|
|
|
|
var testGraphs = []struct {
|
|
g string
|
|
bin string
|
|
want []set
|
|
}{
|
|
// Wanted graphs were obtained from showg using the input graph string.
|
|
// The showg output is included for comparison.
|
|
//
|
|
// showg is available here: https://hog.grinvin.org/data/generators/decoders/showg
|
|
{
|
|
// Graph 1, order 0.
|
|
g: "?",
|
|
bin: "0:0",
|
|
want: []set{},
|
|
},
|
|
{
|
|
// Graph 1, order 5.
|
|
// 0 : 2 4;
|
|
// 1 : 3;
|
|
// 2 : 0;
|
|
// 3 : 1 4;
|
|
// 4 : 0 3;
|
|
g: "DQc",
|
|
bin: "5:0100101001",
|
|
want: []set{
|
|
0: linksToInt(2, 4),
|
|
1: linksToInt(3),
|
|
2: linksToInt(0),
|
|
3: linksToInt(1, 4),
|
|
4: linksToInt(0, 3),
|
|
},
|
|
},
|
|
{
|
|
// Graph 1, order 4.
|
|
// 0 : 1 2 3;
|
|
// 1 : 0 2 3;
|
|
// 2 : 0 1 3;
|
|
// 3 : 0 1 2;
|
|
g: "C~",
|
|
bin: "4:111111",
|
|
want: []set{
|
|
0: linksToInt(1, 2, 3),
|
|
1: linksToInt(0, 2, 3),
|
|
2: linksToInt(0, 1, 3),
|
|
3: linksToInt(0, 1, 2),
|
|
},
|
|
},
|
|
{
|
|
// Graph 1, order 6.
|
|
// 0 : 1 3 4;
|
|
// 1 : 0 2 5;
|
|
// 2 : 1 3 4;
|
|
// 3 : 0 2 5;
|
|
// 4 : 0 2 5;
|
|
// 5 : 1 3 4;
|
|
g: "ElhW",
|
|
bin: "6:101101101001011",
|
|
want: []set{
|
|
0: linksToInt(1, 3, 4),
|
|
1: linksToInt(0, 2, 5),
|
|
2: linksToInt(1, 3, 4),
|
|
3: linksToInt(0, 2, 5),
|
|
4: linksToInt(0, 2, 5),
|
|
5: linksToInt(1, 3, 4),
|
|
},
|
|
},
|
|
{
|
|
// Graph 1, order 10.
|
|
// 0 : 1 2 3;
|
|
// 1 : 0 4 5;
|
|
// 2 : 0 6 7;
|
|
// 3 : 0 8 9;
|
|
// 4 : 1 6 8;
|
|
// 5 : 1 7 9;
|
|
// 6 : 2 4 9;
|
|
// 7 : 2 5 8;
|
|
// 8 : 3 4 7;
|
|
// 9 : 3 5 6;
|
|
g: "IsP@PGXD_",
|
|
bin: "10:110100010001000001010001001000011001000101100",
|
|
want: []set{
|
|
0: linksToInt(1, 2, 3),
|
|
1: linksToInt(0, 4, 5),
|
|
2: linksToInt(0, 6, 7),
|
|
3: linksToInt(0, 8, 9),
|
|
4: linksToInt(1, 6, 8),
|
|
5: linksToInt(1, 7, 9),
|
|
6: linksToInt(2, 4, 9),
|
|
7: linksToInt(2, 5, 8),
|
|
8: linksToInt(3, 4, 7),
|
|
9: linksToInt(3, 5, 6),
|
|
},
|
|
},
|
|
{
|
|
// Graph 1, order 17.
|
|
// 0 : 1 15 16;
|
|
// 1 : 0 2 5;
|
|
// 2 : 1 3 14;
|
|
// 3 : 2 4 16;
|
|
// 4 : 3 5 15;
|
|
// 5 : 1 4 6;
|
|
// 6 : 5 7 16;
|
|
// 7 : 6 8 11;
|
|
// 8 : 7 9 13;
|
|
// 9 : 8 10 16;
|
|
// 10 : 9 11 14;
|
|
// 11 : 7 10 12;
|
|
// 12 : 11 13 16;
|
|
// 13 : 8 12 14;
|
|
// 14 : 2 10 13 15;
|
|
// 15 : 0 4 14;
|
|
// 16 : 0 3 6 9 12;
|
|
g: "PhDGGC@?G?_H?@?Gc@KO@cc_",
|
|
bin: "17:1010010001010010000010000001000000010000000010000000001000000010010000000000010000000010001001000000010011000100000000011001001001001000",
|
|
want: []set{
|
|
0: linksToInt(1, 15, 16),
|
|
1: linksToInt(0, 2, 5),
|
|
2: linksToInt(1, 3, 14),
|
|
3: linksToInt(2, 4, 16),
|
|
4: linksToInt(3, 5, 15),
|
|
5: linksToInt(1, 4, 6),
|
|
6: linksToInt(5, 7, 16),
|
|
7: linksToInt(6, 8, 11),
|
|
8: linksToInt(7, 9, 13),
|
|
9: linksToInt(8, 10, 16),
|
|
10: linksToInt(9, 11, 14),
|
|
11: linksToInt(7, 10, 12),
|
|
12: linksToInt(11, 13, 16),
|
|
13: linksToInt(8, 12, 14),
|
|
14: linksToInt(2, 10, 13, 15),
|
|
15: linksToInt(0, 4, 14),
|
|
16: linksToInt(0, 3, 6, 9, 12),
|
|
},
|
|
},
|
|
}
|
|
|
|
func TestNumberOf(t *testing.T) {
|
|
for _, test := range testGraphs {
|
|
n := numberOf(Graph(test.g))
|
|
if n != int64(len(test.want)) {
|
|
t.Errorf("unexpected graph n: got:%d want:%d", n, len(test.want))
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGoString(t *testing.T) {
|
|
for _, test := range testGraphs {
|
|
gosyntax := Graph(test.g).GoString()
|
|
if gosyntax != test.bin {
|
|
t.Errorf("unexpected graph string: got:%s want:%s", gosyntax, test.bin)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestGraph(t *testing.T) {
|
|
for _, test := range testGraphs {
|
|
g := Graph(test.g)
|
|
if !IsValid(g) {
|
|
t.Errorf("unexpected invalid graph %q", g)
|
|
}
|
|
nodes := g.Nodes()
|
|
if nodes.Len() != len(test.want) {
|
|
t.Errorf("unexpected graph n: got:%d want:%d", nodes.Len(), len(test.want))
|
|
}
|
|
got := make([]set, nodes.Len())
|
|
for nodes.Next() {
|
|
n := nodes.Node()
|
|
got[n.ID()] = linksTo(graph.NodesOf(g.From(n.ID()))...)
|
|
}
|
|
if !reflect.DeepEqual(got, test.want) {
|
|
t.Errorf("unexpected graph: got:%v want:%v", got, test.want)
|
|
}
|
|
for i, s := range got {
|
|
f := g.From(int64(i)).Len()
|
|
if f != len(s) {
|
|
t.Errorf("unexpected number of nodes from %d: got:%d want:%d", i, f, len(s))
|
|
}
|
|
}
|
|
|
|
dst := simple.NewUndirectedGraph()
|
|
graph.Copy(dst, g)
|
|
enc := Encode(dst)
|
|
if enc != g {
|
|
t.Errorf("unexpected round trip: got:%q want:%q", enc, g)
|
|
}
|
|
}
|
|
}
|
|
|
|
type set map[int]struct{}
|
|
|
|
func linksToInt(nodes ...int) map[int]struct{} {
|
|
s := make(map[int]struct{})
|
|
for _, n := range nodes {
|
|
s[n] = struct{}{}
|
|
}
|
|
return s
|
|
}
|
|
|
|
func linksTo(nodes ...graph.Node) map[int]struct{} {
|
|
s := make(map[int]struct{})
|
|
for _, n := range nodes {
|
|
s[int(n.ID())] = struct{}{}
|
|
}
|
|
return s
|
|
}
|
|
|
|
func TestLargeEncoding(t *testing.T) {
|
|
for _, l := range []int{
|
|
50, 60, 70, 80, 100, 1e4,
|
|
} {
|
|
g6 := Encode(implicitCycle(l))
|
|
if !IsValid(g6) {
|
|
t.Errorf("graph6-encoding unexpectedly invalid: %v", g6)
|
|
}
|
|
for i, b := range []byte(g6) {
|
|
if b < 63 || 126 < b {
|
|
t.Errorf("graph6-encoding contains invalid character at %d: %q", i, b)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
type implicitCycle int32
|
|
|
|
func (i implicitCycle) Node(id int64) graph.Node {
|
|
if id < int64(i) {
|
|
return node(id)
|
|
}
|
|
return nil
|
|
}
|
|
func (i implicitCycle) Nodes() graph.Nodes {
|
|
return iterator.NewImplicitNodes(0, int(i), func(id int) graph.Node { return node(id) })
|
|
}
|
|
func (i implicitCycle) From(id int64) graph.Nodes {
|
|
if i < 2 {
|
|
return graph.Empty
|
|
}
|
|
// This is not a valid undirected cycle, but it gets bits
|
|
// into the routine for testing and that is all we care about.
|
|
next := int(id+1) % int(i)
|
|
return iterator.NewImplicitNodes(next, next+1, func(id int) graph.Node { return node(id) })
|
|
}
|
|
func (i implicitCycle) HasEdgeBetween(xid, yid int64) bool { return false }
|
|
func (i implicitCycle) Edge(xid, yid int64) graph.Edge { return nil }
|
|
|
|
type node int32
|
|
|
|
func (n node) ID() int64 { return int64(n) }
|