Initial commit: Go 1.23 release state
This commit is contained in:
231
test/typeparam/graph.go
Normal file
231
test/typeparam/graph.go
Normal file
@@ -0,0 +1,231 @@
|
||||
// run
|
||||
|
||||
// Copyright 2021 The Go 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 main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
// _SliceEqual reports whether two slices are equal: the same length and all
|
||||
// elements equal. All floating point NaNs are considered equal.
|
||||
func _SliceEqual[Elem comparable](s1, s2 []Elem) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for i, v1 := range s1 {
|
||||
v2 := s2[i]
|
||||
if v1 != v2 {
|
||||
isNaN := func(f Elem) bool { return f != f }
|
||||
if !isNaN(v1) || !isNaN(v2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// A Graph is a collection of nodes. A node may have an arbitrary number
|
||||
// of edges. An edge connects two nodes. Both nodes and edges must be
|
||||
// comparable. This is an undirected simple graph.
|
||||
type _Graph[_Node _NodeC[_Edge], _Edge _EdgeC[_Node]] struct {
|
||||
nodes []_Node
|
||||
}
|
||||
|
||||
// _NodeC is the constraints on a node in a graph, given the _Edge type.
|
||||
type _NodeC[_Edge any] interface {
|
||||
comparable
|
||||
Edges() []_Edge
|
||||
}
|
||||
|
||||
// _EdgeC is the constraints on an edge in a graph, given the _Node type.
|
||||
type _EdgeC[_Node any] interface {
|
||||
comparable
|
||||
Nodes() (a, b _Node)
|
||||
}
|
||||
|
||||
// _New creates a new _Graph from a collection of Nodes.
|
||||
func _New[_Node _NodeC[_Edge], _Edge _EdgeC[_Node]](nodes []_Node) *_Graph[_Node, _Edge] {
|
||||
return &_Graph[_Node, _Edge]{nodes: nodes}
|
||||
}
|
||||
|
||||
// nodePath holds the path to a node during ShortestPath.
|
||||
// This should ideally be a type defined inside ShortestPath,
|
||||
// but the translator tool doesn't support that.
|
||||
type nodePath[_Node _NodeC[_Edge], _Edge _EdgeC[_Node]] struct {
|
||||
node _Node
|
||||
path []_Edge
|
||||
}
|
||||
|
||||
// ShortestPath returns the shortest path between two nodes,
|
||||
// as an ordered list of edges. If there are multiple shortest paths,
|
||||
// which one is returned is unpredictable.
|
||||
func (g *_Graph[_Node, _Edge]) ShortestPath(from, to _Node) ([]_Edge, error) {
|
||||
visited := make(map[_Node]bool)
|
||||
visited[from] = true
|
||||
workqueue := []nodePath[_Node, _Edge]{nodePath[_Node, _Edge]{from, nil}}
|
||||
for len(workqueue) > 0 {
|
||||
current := workqueue
|
||||
workqueue = nil
|
||||
for _, np := range current {
|
||||
edges := np.node.Edges()
|
||||
for _, edge := range edges {
|
||||
a, b := edge.Nodes()
|
||||
if a == np.node {
|
||||
a = b
|
||||
}
|
||||
if !visited[a] {
|
||||
ve := append([]_Edge(nil), np.path...)
|
||||
ve = append(ve, edge)
|
||||
if a == to {
|
||||
return ve, nil
|
||||
}
|
||||
workqueue = append(workqueue, nodePath[_Node, _Edge]{a, ve})
|
||||
visited[a] = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil, errors.New("no path")
|
||||
}
|
||||
|
||||
type direction int
|
||||
|
||||
const (
|
||||
north direction = iota
|
||||
ne
|
||||
east
|
||||
se
|
||||
south
|
||||
sw
|
||||
west
|
||||
nw
|
||||
up
|
||||
down
|
||||
)
|
||||
|
||||
func (dir direction) String() string {
|
||||
strs := map[direction]string{
|
||||
north: "north",
|
||||
ne: "ne",
|
||||
east: "east",
|
||||
se: "se",
|
||||
south: "south",
|
||||
sw: "sw",
|
||||
west: "west",
|
||||
nw: "nw",
|
||||
up: "up",
|
||||
down: "down",
|
||||
}
|
||||
if str, ok := strs[dir]; ok {
|
||||
return str
|
||||
}
|
||||
return fmt.Sprintf("direction %d", dir)
|
||||
}
|
||||
|
||||
type mazeRoom struct {
|
||||
index int
|
||||
exits [10]int
|
||||
}
|
||||
|
||||
type mazeEdge struct {
|
||||
from, to int
|
||||
dir direction
|
||||
}
|
||||
|
||||
// Edges returns the exits from the room.
|
||||
func (m mazeRoom) Edges() []mazeEdge {
|
||||
var r []mazeEdge
|
||||
for i, exit := range m.exits {
|
||||
if exit != 0 {
|
||||
r = append(r, mazeEdge{
|
||||
from: m.index,
|
||||
to: exit,
|
||||
dir: direction(i),
|
||||
})
|
||||
}
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
// Nodes returns the rooms connected by an edge.
|
||||
//
|
||||
//go:noinline
|
||||
func (e mazeEdge) Nodes() (mazeRoom, mazeRoom) {
|
||||
m1, ok := zork[e.from]
|
||||
if !ok {
|
||||
panic("bad edge")
|
||||
}
|
||||
m2, ok := zork[e.to]
|
||||
if !ok {
|
||||
panic("bad edge")
|
||||
}
|
||||
return m1, m2
|
||||
}
|
||||
|
||||
// The first maze in Zork. Room indexes based on original Fortran data file.
|
||||
// You are in a maze of twisty little passages, all alike.
|
||||
var zork = map[int]mazeRoom{
|
||||
11: {exits: [10]int{north: 11, south: 12, east: 14}}, // west to Troll Room
|
||||
12: {exits: [10]int{south: 11, north: 14, east: 13}},
|
||||
13: {exits: [10]int{west: 12, north: 14, up: 16}},
|
||||
14: {exits: [10]int{west: 13, north: 11, east: 15}},
|
||||
15: {exits: [10]int{south: 14}}, // Dead End
|
||||
16: {exits: [10]int{east: 17, north: 13, sw: 18}}, // skeleton, etc.
|
||||
17: {exits: [10]int{west: 16}}, // Dead End
|
||||
18: {exits: [10]int{down: 16, east: 19, west: 18, up: 22}},
|
||||
19: {exits: [10]int{up: 29, west: 18, ne: 15, east: 20, south: 30}},
|
||||
20: {exits: [10]int{ne: 19, west: 20, se: 21}},
|
||||
21: {exits: [10]int{north: 20}}, // Dead End
|
||||
22: {exits: [10]int{north: 18, east: 24, down: 23, south: 28, west: 26, nw: 22}},
|
||||
23: {exits: [10]int{east: 22, west: 28, up: 24}},
|
||||
24: {exits: [10]int{ne: 25, down: 23, nw: 28, sw: 26}},
|
||||
25: {exits: [10]int{sw: 24}}, // Grating room (up to Clearing)
|
||||
26: {exits: [10]int{west: 16, sw: 24, east: 28, up: 22, north: 27}},
|
||||
27: {exits: [10]int{south: 26}}, // Dead End
|
||||
28: {exits: [10]int{east: 22, down: 26, south: 23, west: 24}},
|
||||
29: {exits: [10]int{west: 30, nw: 29, ne: 19, south: 19}},
|
||||
30: {exits: [10]int{west: 29, south: 19}}, // ne to Cyclops Room
|
||||
}
|
||||
|
||||
func TestShortestPath() {
|
||||
// The Zork maze is not a proper undirected simple graph,
|
||||
// as there are some one way paths (e.g., 19 -> 15),
|
||||
// but for this test that doesn't matter.
|
||||
|
||||
// Set the index field in the map. Simpler than doing it in the
|
||||
// composite literal.
|
||||
for k := range zork {
|
||||
r := zork[k]
|
||||
r.index = k
|
||||
zork[k] = r
|
||||
}
|
||||
|
||||
var nodes []mazeRoom
|
||||
for idx, room := range zork {
|
||||
mridx := room
|
||||
mridx.index = idx
|
||||
nodes = append(nodes, mridx)
|
||||
}
|
||||
g := _New[mazeRoom, mazeEdge](nodes)
|
||||
path, err := g.ShortestPath(zork[11], zork[30])
|
||||
if err != nil {
|
||||
panic(fmt.Sprintf("%v", err))
|
||||
}
|
||||
var steps []direction
|
||||
for _, edge := range path {
|
||||
steps = append(steps, edge.dir)
|
||||
}
|
||||
want := []direction{east, west, up, sw, east, south}
|
||||
if !_SliceEqual(steps, want) {
|
||||
panic(fmt.Sprintf("ShortestPath returned %v, want %v", steps, want))
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
TestShortestPath()
|
||||
}
|
||||
Reference in New Issue
Block a user