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