Files
gonum/graph/flow/interval_test.go

161 lines
3.5 KiB
Go

// Copyright ©2024 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 flow
import (
"testing"
"gonum.org/v1/gonum/graph"
"gonum.org/v1/gonum/graph/encoding/dot"
"gonum.org/v1/gonum/graph/simple"
"gonum.org/v1/gonum/graph/topo"
)
var intervalTests = []struct {
name string
internalEdges []struct{ from, to simple.Node }
externalEdges []struct{ from, to simple.Node }
root int64
internalWant []map[int64]intset
externalWant map[int64]intset
}{
{
// Graph from C. Cifuentes, "Reverse Compilation Techniques", 1994 (figure 6-23)
// Available via https://eprints.qut.edu.au/36820/
name: "cifuentes",
internalEdges: []struct{ from, to simple.Node }{
{1, 2},
{1, 5},
{2, 3},
{2, 4},
{3, 5},
{4, 5},
{5, 6},
{6, 7},
{6, 12},
{7, 8},
{7, 9},
{8, 9},
{8, 10},
{9, 10},
{10, 11},
{12, 13},
{14, 13},
{13, 14},
{14, 15},
{15, 6},
},
externalEdges: []struct{ from, to simple.Node }{
{0, 1},
{1, 2},
{2, 1},
},
root: 1,
internalWant: []map[int64]intset{
{
1: linksTo(2, 5),
2: linksTo(3, 4),
3: linksTo(5),
4: linksTo(5),
5: nil,
},
{
6: linksTo(7, 12),
7: linksTo(8, 9),
8: linksTo(9, 10),
9: linksTo(10),
10: linksTo(11),
11: nil,
12: nil,
},
{
13: linksTo(14),
14: linksTo(13, 15),
15: nil,
},
},
externalWant: map[int64]intset{
0: linksTo(1),
1: linksTo(2),
2: linksTo(1),
},
},
}
func TestInterval(t *testing.T) {
for _, test := range intervalTests {
t.Run(test.name, func(t *testing.T) {
g := simple.NewDirectedGraph()
for _, e := range test.internalEdges {
g.SetEdge(simple.Edge{F: e.from, T: e.to})
}
ig := Intervals(g, test.root)
if len(ig.Intervals) != len(test.internalWant) {
t.Fatalf("unexpected interval count: got:%d want:%d", len(ig.Intervals), len(test.internalWant))
}
var igGot graph.Directed = &ig
igWant := gFromIntsets(test.externalWant)
if !topo.Equal(igGot, igWant) {
igGotDot, err := dot.Marshal(&ig, "", "", "\t")
if err != nil {
t.Fatalf("unexpected error marshalling got DOT: %v", err)
}
igWantDot, err := dot.Marshal(igWant, "", "", "\t")
if err != nil {
t.Fatalf("unexpected error marshalling want DOT: %v", err)
}
t.Errorf("unexpected topology of interval graph:\ngot:\n%s\nwant:\n%s", igGotDot, igWantDot)
}
for i, iv := range ig.Intervals {
var got graph.Directed = iv
iWant := gFromIntsets(test.internalWant[i])
if !topo.Equal(got, iWant) {
iGotDot, err := dot.Marshal(iv, "", "", "\t")
if err != nil {
t.Fatalf("unexpected error marshalling got DOT: %v", err)
}
iWantDot, err := dot.Marshal(iWant, "", "", "\t")
if err != nil {
t.Fatalf("unexpected error marshalling want DOT: %v", err)
}
t.Errorf("unexpected topology of interval %d:\ngot:\n%s\nwant:\n%s", i, iGotDot, iWantDot)
}
}
})
}
}
func gFromIntsets(s map[int64]intset) graph.Directed {
g := simple.NewDirectedGraph()
for u, e := range s {
// Add nodes that are not defined by an edge.
if g.Node(int64(u)) == nil {
g.AddNode(simple.Node(u))
}
for v := range e {
g.SetEdge(simple.Edge{F: simple.Node(u), T: simple.Node(v)})
}
}
return g
}
// intset is an integer set.
type intset map[int64]struct{}
func linksTo(i ...int64) intset {
if len(i) == 0 {
return nil
}
s := make(intset)
for _, v := range i {
s[v] = struct{}{}
}
return s
}