| // Copyright 2026 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 graph |
| |
| import "iter" |
| |
| // A CompactGraph is a Graph with nodes that are compactly numbered from [0, |
| // NumNodes()). |
| // |
| // Compactly numbered graphs are useful for many graph algorithms, and many |
| // graph representations are naturally compact. |
| // |
| // To compact an arbitrary graph, use [Compact]. |
| type CompactGraph interface { |
| Graph[int] |
| IsCompact() |
| } |
| |
| // A nodePreserving graph is a transformation of another graph that preserves |
| // node IDs. |
| type nodePreserving interface { |
| Graph[int] |
| unwrapPreservingNodes() Graph[int] |
| } |
| |
| // Compact takes a Graph with arbitrary NodeIDs and returns a compact graph. |
| // |
| // If g implements [CompactGraph], it assumes g is already compact and simply |
| // returns g and an identity mapping. |
| func Compact[NodeID comparable](g Graph[NodeID]) (CompactGraph, *Index[NodeID]) { |
| // If it's already compact, simply return it. |
| if gc, ok := g.(CompactGraph); ok { |
| // The above assertion ensures NodeID is int, so we know this type |
| // assertion will always succeed. |
| return gc, any(NewIdentityIndex(gc.NumNodes())).(*Index[NodeID]) |
| } |
| |
| // If it's a transformation and the underlying graph is compact, we can use |
| // an identity index. Though we still need to build a compactGraph to |
| // satisfy the CompactGraph interface. |
| g2, _ := g.(nodePreserving) |
| for g2 != nil { |
| unwrapped := g2.unwrapPreservingNodes() |
| if gc, ok := unwrapped.(CompactGraph); ok { |
| index := any(NewIdentityIndex(gc.NumNodes())).(*Index[NodeID]) |
| cg := compactGraph[NodeID]{g, index} |
| return &cg, index |
| } |
| g2, _ = unwrapped.(nodePreserving) |
| } |
| |
| // Nope, just build an index. |
| cg := compactGraph[NodeID]{g, NewIndex(g.Nodes())} |
| return &cg, cg.m |
| } |
| |
| type compactGraph[NodeID comparable] struct { |
| g Graph[NodeID] |
| m *Index[NodeID] |
| } |
| |
| func (g *compactGraph[NodeID]) Nodes() iter.Seq[int] { |
| return func(yield func(int) bool) { |
| for i := range g.g.NumNodes() { |
| if !yield(i) { |
| break |
| } |
| } |
| } |
| } |
| |
| func (g *compactGraph[NodeID]) NumNodes() int { |
| return g.g.NumNodes() |
| } |
| |
| func (g *compactGraph[NodeID]) Out(node int) iter.Seq[int] { |
| id := g.m.Value(node) |
| return func(yield func(int) bool) { |
| for nid := range g.g.Out(id) { |
| if !yield(g.m.Index(nid)) { |
| break |
| } |
| } |
| } |
| } |
| |
| func (g *compactGraph[NodeID]) IsCompact() {} |