File: gen.go

package info (click to toggle)
golang-gonum-v1-gonum 0.15.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 18,792 kB
  • sloc: asm: 6,252; fortran: 5,271; sh: 377; ruby: 211; makefile: 98
file content (241 lines) | stat: -rw-r--r-- 6,356 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
// Copyright ©2015 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 gen

import (
	"fmt"

	"gonum.org/v1/gonum/graph"
)

// GraphBuilder is a graph that can have nodes and edges added.
type GraphBuilder interface {
	HasEdgeBetween(xid, yid int64) bool
	graph.Builder
}

func abs(a int) int {
	if a < 0 {
		return -a
	}
	return a
}

// NodeIDGraphBuilder is a graph that can create new nodes with
// specified IDs.
type NodeIDGraphBuilder interface {
	graph.Builder
	graph.NodeWithIDer
}

// IDer is a mapping from an index to a node ID.
type IDer interface {
	// Len returns the length of the set of node IDs.
	Len() int

	// ID returns the ID of the indexed node.
	// ID must be a bijective function. No check
	// is made for this property.
	ID(int) int64
}

// IDRange is an IDer that provides a set of IDs in [First, Last].
type IDRange struct{ First, Last int64 }

func (r IDRange) Len() int       { return int(r.Last - r.First + 1) }
func (r IDRange) ID(i int) int64 { return r.First + int64(i) }

// IDSet is an IDer providing an explicit set of IDs.
type IDSet []int64

func (s IDSet) Len() int       { return len(s) }
func (s IDSet) ID(i int) int64 { return s[i] }

// Complete constructs a complete graph in dst using nodes with the given IDs.
// If any ID appears twice in ids, Complete will panic.
func Complete(dst NodeIDGraphBuilder, ids IDer) {
	switch ids.Len() {
	case 0:
		return
	case 1:
		u, new := dst.NodeWithID(ids.ID(0))
		if new {
			dst.AddNode(u)
		}
		return
	}
	for i := 0; i < ids.Len(); i++ {
		uid := ids.ID(i)
		u, _ := dst.NodeWithID(uid)
		for j := i + 1; j < ids.Len(); j++ {
			vid := ids.ID(j)
			if uid == vid {
				panic(fmt.Errorf("gen: node ID collision i=%d j=%d: id=%d", i, j, uid))
			}
			v, _ := dst.NodeWithID(vid)
			dst.SetEdge(dst.NewEdge(u, v))
		}
	}
}

// Cycle constructs a cycle in dst using the node IDs in cycle.
// If dst is a directed graph, edges are directed from earlier nodes to later
// nodes in cycle. If any ID appears twice in cycle, Cycle will panic.
func Cycle(dst NodeIDGraphBuilder, cycle IDer) {
	switch cycle.Len() {
	case 0:
		return
	case 1:
		u, new := dst.NodeWithID(cycle.ID(0))
		if new {
			dst.AddNode(u)
		}
		return
	}
	err := check(cycle)
	if err != nil {
		panic(err)
	}
	cycleNoCheck(dst, cycle)
}

func cycleNoCheck(dst NodeIDGraphBuilder, cycle IDer) {
	for i := 0; i < cycle.Len(); i++ {
		uid := cycle.ID(i)
		vid := cycle.ID((i + 1) % cycle.Len())
		u, _ := dst.NodeWithID(uid)
		v, _ := dst.NodeWithID(vid)
		dst.SetEdge(dst.NewEdge(u, v))
	}
}

// Path constructs a path graph in dst with
// If dst is a directed graph, edges are directed from earlier nodes to later
// nodes in path. If any ID appears twice in path, Path will panic.
func Path(dst NodeIDGraphBuilder, path IDer) {
	switch path.Len() {
	case 0:
		return
	case 1:
		u, new := dst.NodeWithID(path.ID(0))
		if new {
			dst.AddNode(u)
		}
		return
	}
	err := check(path)
	if err != nil {
		panic(err)
	}
	for i := 0; i < path.Len()-1; i++ {
		uid := path.ID(i)
		vid := path.ID(i + 1)
		u, _ := dst.NodeWithID(uid)
		v, _ := dst.NodeWithID(vid)
		dst.SetEdge(dst.NewEdge(u, v))
	}
}

// Star constructs a star graph in dst with edges between the center node ID to
// node with IDs specified in leaves.
// If dst is a directed graph, edges are directed from the center node to the
// leaves. If any ID appears twice in leaves and center, Star will panic.
func Star(dst NodeIDGraphBuilder, center int64, leaves IDer) {
	c, new := dst.NodeWithID(center)
	if new {
		dst.AddNode(c)
	}
	if leaves.Len() == 0 {
		return
	}
	err := check(leaves, center)
	if err != nil {
		panic(err)
	}
	for i := 0; i < leaves.Len(); i++ {
		id := leaves.ID(i)
		n, _ := dst.NodeWithID(id)
		dst.SetEdge(dst.NewEdge(c, n))
	}
}

// Wheel constructs a wheel graph in dst with edges from the center
// node ID to node with IDs specified in cycle and between nodes with IDs
// adjacent in the cycle.
// If dst is a directed graph, edges are directed from the center node to the
// cycle and from earlier nodes to later nodes in cycle. If any ID appears
// twice in cycle and center, Wheel will panic.
func Wheel(dst NodeIDGraphBuilder, center int64, cycle IDer) {
	Star(dst, center, cycle)
	if cycle.Len() <= 1 {
		return
	}
	cycleNoCheck(dst, cycle)
}

// Tree constructs an n-ary tree breadth-first in dst with the given fan-out, n.
// If the number of nodes does not equal \sum_{i=0}^h n^i, where h is an integer
// indicating the height of the tree, a partial tree will be constructed with not
// all nodes having zero or n children, and not all leaves being h from the root.
// If the number of nodes is greater than one, n must be non-zero and
// less than the number of nodes, otherwise Tree will panic.
// If dst is a directed graph, edges are directed from the root to the leaves.
// If any ID appears more than once in nodes, Tree will panic.
func Tree(dst NodeIDGraphBuilder, n int, nodes IDer) {
	switch nodes.Len() {
	case 0:
		return
	case 1:
		if u, new := dst.NodeWithID(nodes.ID(0)); new {
			dst.AddNode(u)
		}
		return
	}

	if n < 1 || nodes.Len() <= n {
		panic("gen: invalid fan-out")
	}

	err := check(nodes)
	if err != nil {
		panic(err)
	}

	j := 0
	for i := 0; j < nodes.Len(); i++ {
		u, _ := dst.NodeWithID(nodes.ID(i))
		for j = n*i + 1; j <= n*i+n && j < nodes.Len(); j++ {
			v, _ := dst.NodeWithID(nodes.ID(j))
			dst.SetEdge(dst.NewEdge(u, v))
		}
	}
}

// check confirms that no node ID exists more than once in ids and extra.
func check(ids IDer, extra ...int64) error {
	seen := make(map[int64]int, ids.Len()+len(extra))
	for j := 0; j < ids.Len(); j++ {
		uid := ids.ID(j)
		if prev, exists := seen[uid]; exists {
			return fmt.Errorf("gen: node ID collision i=%d j=%d: id=%d", prev, j, uid)
		}
		seen[uid] = j
	}
	lenIDs := ids.Len()
	for j, uid := range extra {
		if prev, exists := seen[uid]; exists {
			if prev < lenIDs {
				if len(extra) == 1 {
					return fmt.Errorf("gen: node ID collision i=%d with extra: id=%d", prev, uid)
				}
				return fmt.Errorf("gen: node ID collision i=%d with extra j=%d: id=%d", prev, j, uid)
			}
			prev -= lenIDs
			return fmt.Errorf("gen: extra node ID collision i=%d j=%d: id=%d", prev, j, uid)
		}
		seen[uid] = j + lenIDs
	}
	return nil
}