// 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) }