/** * @experimental * @since 3.18.0 */ import * as Data from "./Data.js"; import * as Equal from "./Equal.js"; import type { Inspectable } from "./Inspectable.js"; import * as Option from "./Option.js"; import type { Pipeable } from "./Pipeable.js"; /** * Unique identifier for Graph instances. * * @since 3.18.0 * @category symbol */ export declare const TypeId: "~effect/Graph"; /** * Type identifier for Graph instances. * * @since 3.18.0 * @category symbol */ export type TypeId = typeof TypeId; /** * Node index for node identification using plain numbers. * * @since 3.18.0 * @category models */ export type NodeIndex = number; /** * Edge index for edge identification using plain numbers. * * @since 3.18.0 * @category models */ export type EdgeIndex = number; /** * Edge data containing source, target, and user data. * * @since 3.18.0 * @category models */ export declare class Edge extends Data.Class<{ readonly source: NodeIndex; readonly target: NodeIndex; readonly data: E; }> { } /** * Graph type for distinguishing directed and undirected graphs. * * @since 3.18.0 * @category models */ export type Kind = "directed" | "undirected"; /** * Graph prototype interface. * * @since 3.18.0 * @category models */ export interface Proto extends Iterable, Equal.Equal, Pipeable, Inspectable { readonly [TypeId]: TypeId; readonly nodes: Map; readonly edges: Map>; readonly adjacency: Map>; readonly reverseAdjacency: Map>; nextNodeIndex: NodeIndex; nextEdgeIndex: EdgeIndex; isAcyclic: Option.Option; } /** * Immutable graph interface. * * @since 3.18.0 * @category models */ export interface Graph extends Proto { readonly type: T; readonly mutable: false; } /** * Mutable graph interface. * * @since 3.18.0 * @category models */ export interface MutableGraph extends Proto { readonly type: T; readonly mutable: true; } /** * Directed graph type alias. * * @since 3.18.0 * @category models */ export type DirectedGraph = Graph; /** * Undirected graph type alias. * * @since 3.18.0 * @category models */ export type UndirectedGraph = Graph; /** * Mutable directed graph type alias. * * @since 3.18.0 * @category models */ export type MutableDirectedGraph = MutableGraph; /** * Mutable undirected graph type alias. * * @since 3.18.0 * @category models */ export type MutableUndirectedGraph = MutableGraph; /** * Creates a directed graph, optionally with initial mutations. * * @example * ```ts * import { Graph } from "effect" * * // Directed graph with initial nodes and edges * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, "A->B") * Graph.addEdge(mutable, b, c, "B->C") * }) * ``` * * @since 3.18.0 * @category constructors */ export declare const directed: (mutate?: (mutable: MutableDirectedGraph) => void) => DirectedGraph; /** * Creates an undirected graph, optionally with initial mutations. * * @example * ```ts * import { Graph } from "effect" * * // Undirected graph with initial nodes and edges * const graph = Graph.undirected((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, "A-B") * Graph.addEdge(mutable, b, c, "B-C") * }) * ``` * * @since 3.18.0 * @category constructors */ export declare const undirected: (mutate?: (mutable: MutableUndirectedGraph) => void) => UndirectedGraph; /** * Creates a mutable scope for safe graph mutations by copying the data structure. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed() * const mutable = Graph.beginMutation(graph) * // Now mutable can be safely modified without affecting original graph * ``` * * @since 3.18.0 * @category mutations */ export declare const beginMutation: (graph: Graph) => MutableGraph; /** * Converts a mutable graph back to an immutable graph, ending the mutation scope. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed() * const mutable = Graph.beginMutation(graph) * // ... perform mutations on mutable ... * const newGraph = Graph.endMutation(mutable) * ``` * * @since 3.18.0 * @category mutations */ export declare const endMutation: (mutable: MutableGraph) => Graph; /** * Performs scoped mutations on a graph, automatically managing the mutation lifecycle. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed() * const newGraph = Graph.mutate(graph, (mutable) => { * // Safe mutations go here * // mutable gets automatically converted back to immutable * }) * ``` * * @since 3.18.0 * @category mutations */ export declare const mutate: { /** * Performs scoped mutations on a graph, automatically managing the mutation lifecycle. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed() * const newGraph = Graph.mutate(graph, (mutable) => { * // Safe mutations go here * // mutable gets automatically converted back to immutable * }) * ``` * * @since 3.18.0 * @category mutations */ (f: (mutable: MutableGraph) => void): (graph: Graph) => Graph; /** * Performs scoped mutations on a graph, automatically managing the mutation lifecycle. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed() * const newGraph = Graph.mutate(graph, (mutable) => { * // Safe mutations go here * // mutable gets automatically converted back to immutable * }) * ``` * * @since 3.18.0 * @category mutations */ (graph: Graph, f: (mutable: MutableGraph) => void): Graph; }; /** * Adds a new node to a mutable graph and returns its index. * * @example * ```ts * import { Graph } from "effect" * * const result = Graph.mutate(Graph.directed(), (mutable) => { * const nodeA = Graph.addNode(mutable, "Node A") * const nodeB = Graph.addNode(mutable, "Node B") * console.log(nodeA) // NodeIndex with value 0 * console.log(nodeB) // NodeIndex with value 1 * }) * ``` * * @since 3.18.0 * @category mutations */ export declare const addNode: (mutable: MutableGraph, data: N) => NodeIndex; /** * Gets the data associated with a node index, if it exists. * * @example * ```ts * import { Graph, Option } from "effect" * * const graph = Graph.mutate(Graph.directed(), (mutable) => { * Graph.addNode(mutable, "Node A") * }) * * const nodeIndex = 0 * const nodeData = Graph.getNode(graph, nodeIndex) * * if (Option.isSome(nodeData)) { * console.log(nodeData.value) // "Node A" * } * ``` * * @since 3.18.0 * @category getters */ export declare const getNode: (graph: Graph | MutableGraph, nodeIndex: NodeIndex) => Option.Option; /** * Checks if a node with the given index exists in the graph. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.mutate(Graph.directed(), (mutable) => { * Graph.addNode(mutable, "Node A") * }) * * const nodeIndex = 0 * const exists = Graph.hasNode(graph, nodeIndex) * console.log(exists) // true * * const nonExistentIndex = 999 * const notExists = Graph.hasNode(graph, nonExistentIndex) * console.log(notExists) // false * ``` * * @since 3.18.0 * @category getters */ export declare const hasNode: (graph: Graph | MutableGraph, nodeIndex: NodeIndex) => boolean; /** * Returns the number of nodes in the graph. * * @example * ```ts * import { Graph } from "effect" * * const emptyGraph = Graph.directed() * console.log(Graph.nodeCount(emptyGraph)) // 0 * * const graphWithNodes = Graph.mutate(emptyGraph, (mutable) => { * Graph.addNode(mutable, "Node A") * Graph.addNode(mutable, "Node B") * Graph.addNode(mutable, "Node C") * }) * * console.log(Graph.nodeCount(graphWithNodes)) // 3 * ``` * * @since 3.18.0 * @category getters */ export declare const nodeCount: (graph: Graph | MutableGraph) => number; /** * Finds the first node that matches the given predicate. * * @example * ```ts * import { Graph, Option } from "effect" * * const graph = Graph.mutate(Graph.directed(), (mutable) => { * Graph.addNode(mutable, "Node A") * Graph.addNode(mutable, "Node B") * Graph.addNode(mutable, "Node C") * }) * * const result = Graph.findNode(graph, (data) => data.startsWith("Node B")) * console.log(result) // Option.some(1) * * const notFound = Graph.findNode(graph, (data) => data === "Node D") * console.log(notFound) // Option.none() * ``` * * @since 3.18.0 * @category getters */ export declare const findNode: (graph: Graph | MutableGraph, predicate: (data: N) => boolean) => Option.Option; /** * Finds all nodes that match the given predicate. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.mutate(Graph.directed(), (mutable) => { * Graph.addNode(mutable, "Start A") * Graph.addNode(mutable, "Node B") * Graph.addNode(mutable, "Start C") * }) * * const result = Graph.findNodes(graph, (data) => data.startsWith("Start")) * console.log(result) // [0, 2] * * const empty = Graph.findNodes(graph, (data) => data === "Not Found") * console.log(empty) // [] * ``` * * @since 3.18.0 * @category getters */ export declare const findNodes: (graph: Graph | MutableGraph, predicate: (data: N) => boolean) => Array; /** * Finds the first edge that matches the given predicate. * * @example * ```ts * import { Graph, Option } from "effect" * * const graph = Graph.mutate(Graph.directed(), (mutable) => { * const nodeA = Graph.addNode(mutable, "Node A") * const nodeB = Graph.addNode(mutable, "Node B") * const nodeC = Graph.addNode(mutable, "Node C") * Graph.addEdge(mutable, nodeA, nodeB, 10) * Graph.addEdge(mutable, nodeB, nodeC, 20) * }) * * const result = Graph.findEdge(graph, (data) => data > 15) * console.log(result) // Option.some(1) * * const notFound = Graph.findEdge(graph, (data) => data > 100) * console.log(notFound) // Option.none() * ``` * * @since 3.18.0 * @category getters */ export declare const findEdge: (graph: Graph | MutableGraph, predicate: (data: E, source: NodeIndex, target: NodeIndex) => boolean) => Option.Option; /** * Finds all edges that match the given predicate. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.mutate(Graph.directed(), (mutable) => { * const nodeA = Graph.addNode(mutable, "Node A") * const nodeB = Graph.addNode(mutable, "Node B") * const nodeC = Graph.addNode(mutable, "Node C") * Graph.addEdge(mutable, nodeA, nodeB, 10) * Graph.addEdge(mutable, nodeB, nodeC, 20) * Graph.addEdge(mutable, nodeC, nodeA, 30) * }) * * const result = Graph.findEdges(graph, (data) => data >= 20) * console.log(result) // [1, 2] * * const empty = Graph.findEdges(graph, (data) => data > 100) * console.log(empty) // [] * ``` * * @since 3.18.0 * @category getters */ export declare const findEdges: (graph: Graph | MutableGraph, predicate: (data: E, source: NodeIndex, target: NodeIndex) => boolean) => Array; /** * Updates a single node's data by applying a transformation function. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * Graph.addNode(mutable, "Node A") * Graph.addNode(mutable, "Node B") * Graph.updateNode(mutable, 0, (data) => data.toUpperCase()) * }) * * const nodeData = Graph.getNode(graph, 0) * console.log(nodeData) // Option.some("NODE A") * ``` * * @since 3.18.0 * @category transformations */ export declare const updateNode: (mutable: MutableGraph, index: NodeIndex, f: (data: N) => N) => void; /** * Updates a single edge's data by applying a transformation function. * * @example * ```ts * import { Graph } from "effect" * * const result = Graph.mutate(Graph.directed(), (mutable) => { * const nodeA = Graph.addNode(mutable, "Node A") * const nodeB = Graph.addNode(mutable, "Node B") * const edgeIndex = Graph.addEdge(mutable, nodeA, nodeB, 10) * Graph.updateEdge(mutable, edgeIndex, (data) => data * 2) * }) * * const edgeData = Graph.getEdge(result, 0) * console.log(edgeData) // Option.some({ source: 0, target: 1, data: 20 }) * ``` * * @since 3.18.0 * @category mutations */ export declare const updateEdge: (mutable: MutableGraph, edgeIndex: EdgeIndex, f: (data: E) => E) => void; /** * Creates a new graph with transformed node data using the provided mapping function. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * Graph.addNode(mutable, "node a") * Graph.addNode(mutable, "node b") * Graph.addNode(mutable, "node c") * Graph.mapNodes(mutable, (data) => data.toUpperCase()) * }) * * const nodeData = Graph.getNode(graph, 0) * console.log(nodeData) // Option.some("NODE A") * ``` * * @since 3.18.0 * @category transformations */ export declare const mapNodes: (mutable: MutableGraph, f: (data: N) => N) => void; /** * Transforms all edge data in a mutable graph using the provided mapping function. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, 10) * Graph.addEdge(mutable, b, c, 20) * Graph.mapEdges(mutable, (data) => data * 2) * }) * * const edgeData = Graph.getEdge(graph, 0) * console.log(edgeData) // Option.some({ source: 0, target: 1, data: 20 }) * ``` * * @since 3.18.0 * @category transformations */ export declare const mapEdges: (mutable: MutableGraph, f: (data: E) => E) => void; /** * Reverses all edge directions in a mutable graph by swapping source and target nodes. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, 1) // A -> B * Graph.addEdge(mutable, b, c, 2) // B -> C * Graph.reverse(mutable) // Now B -> A, C -> B * }) * * const edge0 = Graph.getEdge(graph, 0) * console.log(edge0) // Option.some({ source: 1, target: 0, data: 1 }) - B -> A * ``` * * @since 3.18.0 * @category transformations */ export declare const reverse: (mutable: MutableGraph) => void; /** * Filters and optionally transforms nodes in a mutable graph using a predicate function. * Nodes that return Option.none are removed along with all their connected edges. * * @example * ```ts * import { Graph, Option } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "active") * const b = Graph.addNode(mutable, "inactive") * const c = Graph.addNode(mutable, "active") * Graph.addEdge(mutable, a, b, 1) * Graph.addEdge(mutable, b, c, 2) * * // Keep only "active" nodes and transform to uppercase * Graph.filterMapNodes(mutable, (data) => * data === "active" ? Option.some(data.toUpperCase()) : Option.none() * ) * }) * * console.log(Graph.nodeCount(graph)) // 2 (only "active" nodes remain) * ``` * * @since 3.18.0 * @category transformations */ export declare const filterMapNodes: (mutable: MutableGraph, f: (data: N) => Option.Option) => void; /** * Filters and optionally transforms edges in a mutable graph using a predicate function. * Edges that return Option.none are removed from the graph. * * @example * ```ts * import { Graph, Option } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, 5) * Graph.addEdge(mutable, b, c, 15) * Graph.addEdge(mutable, c, a, 25) * * // Keep only edges with weight >= 10 and double their weight * Graph.filterMapEdges(mutable, (data) => * data >= 10 ? Option.some(data * 2) : Option.none() * ) * }) * * console.log(Graph.edgeCount(graph)) // 2 (edges with weight 5 removed) * ``` * * @since 3.18.0 * @category transformations */ export declare const filterMapEdges: (mutable: MutableGraph, f: (data: E) => Option.Option) => void; /** * Filters nodes by removing those that don't match the predicate. * This function modifies the mutable graph in place. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * Graph.addNode(mutable, "active") * Graph.addNode(mutable, "inactive") * Graph.addNode(mutable, "pending") * Graph.addNode(mutable, "active") * * // Keep only "active" nodes * Graph.filterNodes(mutable, (data) => data === "active") * }) * * console.log(Graph.nodeCount(graph)) // 2 (only "active" nodes remain) * ``` * * @since 3.18.0 * @category transformations */ export declare const filterNodes: (mutable: MutableGraph, predicate: (data: N) => boolean) => void; /** * Filters edges by removing those that don't match the predicate. * This function modifies the mutable graph in place. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * * Graph.addEdge(mutable, a, b, 5) * Graph.addEdge(mutable, b, c, 15) * Graph.addEdge(mutable, c, a, 25) * * // Keep only edges with weight >= 10 * Graph.filterEdges(mutable, (data) => data >= 10) * }) * * console.log(Graph.edgeCount(graph)) // 2 (edge with weight 5 removed) * ``` * * @since 3.18.0 * @category transformations */ export declare const filterEdges: (mutable: MutableGraph, predicate: (data: E) => boolean) => void; /** * Adds a new edge to a mutable graph and returns its index. * * @example * ```ts * import { Graph } from "effect" * * const result = Graph.mutate(Graph.directed(), (mutable) => { * const nodeA = Graph.addNode(mutable, "Node A") * const nodeB = Graph.addNode(mutable, "Node B") * const edge = Graph.addEdge(mutable, nodeA, nodeB, 42) * console.log(edge) // EdgeIndex with value 0 * }) * ``` * * @since 3.18.0 * @category mutations */ export declare const addEdge: (mutable: MutableGraph, source: NodeIndex, target: NodeIndex, data: E) => EdgeIndex; /** * Removes a node and all its incident edges from a mutable graph. * * @example * ```ts * import { Graph } from "effect" * * const result = Graph.mutate(Graph.directed(), (mutable) => { * const nodeA = Graph.addNode(mutable, "Node A") * const nodeB = Graph.addNode(mutable, "Node B") * Graph.addEdge(mutable, nodeA, nodeB, 42) * * // Remove nodeA and all edges connected to it * Graph.removeNode(mutable, nodeA) * }) * ``` * * @since 3.18.0 * @category mutations */ export declare const removeNode: (mutable: MutableGraph, nodeIndex: NodeIndex) => void; /** * Removes an edge from a mutable graph. * * @example * ```ts * import { Graph } from "effect" * * const result = Graph.mutate(Graph.directed(), (mutable) => { * const nodeA = Graph.addNode(mutable, "Node A") * const nodeB = Graph.addNode(mutable, "Node B") * const edge = Graph.addEdge(mutable, nodeA, nodeB, 42) * * // Remove the edge * Graph.removeEdge(mutable, edge) * }) * ``` * * @since 3.18.0 * @category mutations */ export declare const removeEdge: (mutable: MutableGraph, edgeIndex: EdgeIndex) => void; /** * Gets the edge data associated with an edge index, if it exists. * * @example * ```ts * import { Graph, Option } from "effect" * * const graph = Graph.mutate(Graph.directed(), (mutable) => { * const nodeA = Graph.addNode(mutable, "Node A") * const nodeB = Graph.addNode(mutable, "Node B") * Graph.addEdge(mutable, nodeA, nodeB, 42) * }) * * const edgeIndex = 0 * const edgeData = Graph.getEdge(graph, edgeIndex) * * if (Option.isSome(edgeData)) { * console.log(edgeData.value.data) // 42 * console.log(edgeData.value.source) // NodeIndex(0) * console.log(edgeData.value.target) // NodeIndex(1) * } * ``` * * @since 3.18.0 * @category getters */ export declare const getEdge: (graph: Graph | MutableGraph, edgeIndex: EdgeIndex) => Option.Option>; /** * Checks if an edge exists between two nodes in the graph. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.mutate(Graph.directed(), (mutable) => { * const nodeA = Graph.addNode(mutable, "Node A") * const nodeB = Graph.addNode(mutable, "Node B") * const nodeC = Graph.addNode(mutable, "Node C") * Graph.addEdge(mutable, nodeA, nodeB, 42) * }) * * const nodeA = 0 * const nodeB = 1 * const nodeC = 2 * * const hasAB = Graph.hasEdge(graph, nodeA, nodeB) * console.log(hasAB) // true * * const hasAC = Graph.hasEdge(graph, nodeA, nodeC) * console.log(hasAC) // false * ``` * * @since 3.18.0 * @category getters */ export declare const hasEdge: (graph: Graph | MutableGraph, source: NodeIndex, target: NodeIndex) => boolean; /** * Returns the number of edges in the graph. * * @example * ```ts * import { Graph } from "effect" * * const emptyGraph = Graph.directed() * console.log(Graph.edgeCount(emptyGraph)) // 0 * * const graphWithEdges = Graph.mutate(emptyGraph, (mutable) => { * const nodeA = Graph.addNode(mutable, "Node A") * const nodeB = Graph.addNode(mutable, "Node B") * const nodeC = Graph.addNode(mutable, "Node C") * Graph.addEdge(mutable, nodeA, nodeB, 1) * Graph.addEdge(mutable, nodeB, nodeC, 2) * Graph.addEdge(mutable, nodeC, nodeA, 3) * }) * * console.log(Graph.edgeCount(graphWithEdges)) // 3 * ``` * * @since 3.18.0 * @category getters */ export declare const edgeCount: (graph: Graph | MutableGraph) => number; /** * Returns the neighboring nodes (targets of outgoing edges) for a given node. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.mutate(Graph.directed(), (mutable) => { * const nodeA = Graph.addNode(mutable, "Node A") * const nodeB = Graph.addNode(mutable, "Node B") * const nodeC = Graph.addNode(mutable, "Node C") * Graph.addEdge(mutable, nodeA, nodeB, 1) * Graph.addEdge(mutable, nodeA, nodeC, 2) * }) * * const nodeA = 0 * const nodeB = 1 * const nodeC = 2 * * const neighborsA = Graph.neighbors(graph, nodeA) * console.log(neighborsA) // [NodeIndex(1), NodeIndex(2)] * * const neighborsB = Graph.neighbors(graph, nodeB) * console.log(neighborsB) // [] * ``` * * @since 3.18.0 * @category getters */ export declare const neighbors: (graph: Graph | MutableGraph, nodeIndex: NodeIndex) => Array; /** * Get neighbors of a node in a specific direction for bidirectional traversal. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * Graph.addEdge(mutable, a, b, "A->B") * }) * * const nodeA = 0 * const nodeB = 1 * * // Get outgoing neighbors (nodes that nodeA points to) * const outgoing = Graph.neighborsDirected(graph, nodeA, "outgoing") * * // Get incoming neighbors (nodes that point to nodeB) * const incoming = Graph.neighborsDirected(graph, nodeB, "incoming") * ``` * * @since 3.18.0 * @category queries */ export declare const neighborsDirected: (graph: Graph | MutableGraph, nodeIndex: NodeIndex, direction: Direction) => Array; /** * Exports a graph to GraphViz DOT format for visualization. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.mutate(Graph.directed(), (mutable) => { * const nodeA = Graph.addNode(mutable, "Node A") * const nodeB = Graph.addNode(mutable, "Node B") * const nodeC = Graph.addNode(mutable, "Node C") * Graph.addEdge(mutable, nodeA, nodeB, 1) * Graph.addEdge(mutable, nodeB, nodeC, 2) * Graph.addEdge(mutable, nodeC, nodeA, 3) * }) * * const dot = Graph.toGraphViz(graph) * console.log(dot) * // digraph G { * // "0" [label="Node A"]; * // "1" [label="Node B"]; * // "2" [label="Node C"]; * // "0" -> "1" [label="1"]; * // "1" -> "2" [label="2"]; * // "2" -> "0" [label="3"]; * // } * ``` * * @since 3.18.0 * @category utils */ export declare const toGraphViz: (graph: Graph | MutableGraph, options?: { readonly nodeLabel?: (data: N) => string; readonly edgeLabel?: (data: E) => string; readonly graphName?: string; }) => string; /** * Direction for graph traversal, indicating which edges to follow. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * Graph.addEdge(mutable, a, b, "A->B") * }) * * // Follow outgoing edges (normal direction) * const outgoingNodes = Array.from(Graph.indices(Graph.dfs(graph, { startNodes: [0], direction: "outgoing" }))) * * // Follow incoming edges (reverse direction) * const incomingNodes = Array.from(Graph.indices(Graph.dfs(graph, { startNodes: [1], direction: "incoming" }))) * ``` * * @since 3.18.0 * @category models */ export type Direction = "outgoing" | "incoming"; /** * Checks if the graph is acyclic (contains no cycles). * * Uses depth-first search to detect back edges, which indicate cycles. * For directed graphs, any back edge creates a cycle. For undirected graphs, * a back edge that doesn't go to the immediate parent creates a cycle. * * @example * ```ts * import { Graph } from "effect" * * // Acyclic directed graph (DAG) * const dag = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, "A->B") * Graph.addEdge(mutable, b, c, "B->C") * }) * console.log(Graph.isAcyclic(dag)) // true * * // Cyclic directed graph * const cyclic = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * Graph.addEdge(mutable, a, b, "A->B") * Graph.addEdge(mutable, b, a, "B->A") // Creates cycle * }) * console.log(Graph.isAcyclic(cyclic)) // false * ``` * * @since 3.18.0 * @category algorithms */ export declare const isAcyclic: (graph: Graph | MutableGraph) => boolean; /** * Checks if an undirected graph is bipartite. * * A bipartite graph is one whose vertices can be divided into two disjoint sets * such that no two vertices within the same set are adjacent. Uses BFS coloring * to determine bipartiteness. * * @example * ```ts * import { Graph } from "effect" * * // Bipartite graph (alternating coloring possible) * const bipartite = Graph.undirected((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * const d = Graph.addNode(mutable, "D") * Graph.addEdge(mutable, a, b, "edge") // Set 1: {A, C}, Set 2: {B, D} * Graph.addEdge(mutable, b, c, "edge") * Graph.addEdge(mutable, c, d, "edge") * }) * console.log(Graph.isBipartite(bipartite)) // true * * // Non-bipartite graph (odd cycle) * const triangle = Graph.undirected((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, "edge") * Graph.addEdge(mutable, b, c, "edge") * Graph.addEdge(mutable, c, a, "edge") // Triangle (3-cycle) * }) * console.log(Graph.isBipartite(triangle)) // false * ``` * * @since 3.18.0 * @category algorithms */ export declare const isBipartite: (graph: Graph | MutableGraph) => boolean; /** * Find connected components in an undirected graph. * Each component is represented as an array of node indices. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.undirected((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * const d = Graph.addNode(mutable, "D") * Graph.addEdge(mutable, a, b, "edge") // Component 1: A-B * Graph.addEdge(mutable, c, d, "edge") // Component 2: C-D * }) * * const components = Graph.connectedComponents(graph) * console.log(components) // [[0, 1], [2, 3]] * ``` * * @since 3.18.0 * @category algorithms */ export declare const connectedComponents: (graph: Graph | MutableGraph) => Array>; /** * Find strongly connected components in a directed graph using Kosaraju's algorithm. * Each SCC is represented as an array of node indices. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, "A->B") * Graph.addEdge(mutable, b, c, "B->C") * Graph.addEdge(mutable, c, a, "C->A") // Creates SCC: A-B-C * }) * * const sccs = Graph.stronglyConnectedComponents(graph) * console.log(sccs) // [[0, 1, 2]] * ``` * * @since 3.18.0 * @category algorithms */ export declare const stronglyConnectedComponents: (graph: Graph | MutableGraph) => Array>; /** * Result of a shortest path computation containing the path and total distance. * * @since 3.18.0 * @category models */ export interface PathResult { readonly path: Array; readonly distance: number; readonly edgeWeights: Array; } /** * Find the shortest path between two nodes using Dijkstra's algorithm. * * Dijkstra's algorithm works with non-negative edge weights and finds the shortest * path from a source node to a target node in O((V + E) log V) time complexity. * * @example * ```ts * import { Graph, Option } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, 5) * Graph.addEdge(mutable, a, c, 10) * Graph.addEdge(mutable, b, c, 2) * }) * * const result = Graph.dijkstra(graph, 0, 2, (edgeData) => edgeData) * if (Option.isSome(result)) { * console.log(result.value.path) // [0, 1, 2] - shortest path A->B->C * console.log(result.value.distance) // 7 - total distance * } * ``` * * @since 3.18.0 * @category algorithms */ export declare const dijkstra: (graph: Graph | MutableGraph, source: NodeIndex, target: NodeIndex, edgeWeight: (edgeData: E) => number) => Option.Option>; /** * Result of all-pairs shortest path computation. * * @since 3.18.0 * @category models */ export interface AllPairsResult { readonly distances: Map>; readonly paths: Map | null>>; readonly edgeWeights: Map>>; } /** * Find shortest paths between all pairs of nodes using Floyd-Warshall algorithm. * * Floyd-Warshall algorithm computes shortest paths between all pairs of nodes in O(V³) time. * It can handle negative edge weights and detect negative cycles. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, 3) * Graph.addEdge(mutable, b, c, 2) * Graph.addEdge(mutable, a, c, 7) * }) * * const result = Graph.floydWarshall(graph, (edgeData) => edgeData) * const distanceAToC = result.distances.get(0)?.get(2) // 5 (A->B->C) * const pathAToC = result.paths.get(0)?.get(2) // [0, 1, 2] * ``` * * @since 3.18.0 * @category algorithms */ export declare const floydWarshall: (graph: Graph | MutableGraph, edgeWeight: (edgeData: E) => number) => AllPairsResult; /** * Find the shortest path between two nodes using A* pathfinding algorithm. * * A* is an extension of Dijkstra's algorithm that uses a heuristic function to guide * the search towards the target, potentially finding paths faster than Dijkstra's. * The heuristic must be admissible (never overestimate the actual cost). * * @example * ```ts * import { Graph, Option } from "effect" * * const graph = Graph.directed<{x: number, y: number}, number>((mutable) => { * const a = Graph.addNode(mutable, {x: 0, y: 0}) * const b = Graph.addNode(mutable, {x: 1, y: 0}) * const c = Graph.addNode(mutable, {x: 2, y: 0}) * Graph.addEdge(mutable, a, b, 1) * Graph.addEdge(mutable, b, c, 1) * }) * * // Manhattan distance heuristic * const heuristic = (nodeData: {x: number, y: number}, targetData: {x: number, y: number}) => * Math.abs(nodeData.x - targetData.x) + Math.abs(nodeData.y - targetData.y) * * const result = Graph.astar(graph, 0, 2, (edgeData) => edgeData, heuristic) * if (Option.isSome(result)) { * console.log(result.value.path) // [0, 1, 2] - shortest path * console.log(result.value.distance) // 2 - total distance * } * ``` * * @since 3.18.0 * @category algorithms */ export declare const astar: (graph: Graph | MutableGraph, source: NodeIndex, target: NodeIndex, edgeWeight: (edgeData: E) => number, heuristic: (sourceNodeData: N, targetNodeData: N) => number) => Option.Option>; /** * Find the shortest path between two nodes using Bellman-Ford algorithm. * * Bellman-Ford algorithm can handle negative edge weights and detects negative cycles. * It has O(VE) time complexity, slower than Dijkstra's but more versatile. * Returns Option.none() if a negative cycle is detected that affects the path. * * @example * ```ts * import { Graph, Option } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, -1) // Negative weight allowed * Graph.addEdge(mutable, b, c, 3) * Graph.addEdge(mutable, a, c, 5) * }) * * const result = Graph.bellmanFord(graph, 0, 2, (edgeData) => edgeData) * if (Option.isSome(result)) { * console.log(result.value.path) // [0, 1, 2] - shortest path A->B->C * console.log(result.value.distance) // 2 - total distance * } * ``` * * @since 3.18.0 * @category algorithms */ export declare const bellmanFord: (graph: Graph | MutableGraph, source: NodeIndex, target: NodeIndex, edgeWeight: (edgeData: E) => number) => Option.Option>; /** * Concrete class for iterables that produce [NodeIndex, NodeData] tuples. * * This class provides a common abstraction for all iterables that return node data, * including traversal iterators (DFS, BFS, etc.) and element iterators (nodes, externals). * It uses a mapEntry function pattern for flexible iteration and transformation. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * Graph.addEdge(mutable, a, b, 1) * }) * * // Both traversal and element iterators return NodeWalker * const dfsNodes: Graph.NodeWalker = Graph.dfs(graph, { startNodes: [0] }) * const allNodes: Graph.NodeWalker = Graph.nodes(graph) * * // Common interface for working with node iterables * function processNodes(nodeIterable: Graph.NodeWalker): Array { * return Array.from(Graph.indices(nodeIterable)) * } * * // Access node data using values() or entries() * const nodeData = Array.from(Graph.values(dfsNodes)) // ["A", "B"] * const nodeEntries = Array.from(Graph.entries(allNodes)) // [[0, "A"], [1, "B"]] * ``` * * @since 3.18.0 * @category models */ export declare class Walker implements Iterable<[T, N]> { readonly [Symbol.iterator]: () => Iterator<[T, N]>; /** * Visits each element and maps it to a value using the provided function. * * Takes a function that receives the index and data, * and returns an iterable of the mapped values. Skips elements that * no longer exist in the graph. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * Graph.addEdge(mutable, a, b, 1) * }) * * const dfs = Graph.dfs(graph, { startNodes: [0] }) * * // Map to just the node data * const values = Array.from(dfs.visit((index, data) => data)) * console.log(values) // ["A", "B"] * * // Map to custom objects * const custom = Array.from(dfs.visit((index, data) => ({ id: index, name: data }))) * console.log(custom) // [{ id: 0, name: "A" }, { id: 1, name: "B" }] * ``` * * @since 3.18.0 * @category iterators */ readonly visit: (f: (index: T, data: N) => U) => Iterable; constructor( /** * Visits each element and maps it to a value using the provided function. * * Takes a function that receives the index and data, * and returns an iterable of the mapped values. Skips elements that * no longer exist in the graph. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * Graph.addEdge(mutable, a, b, 1) * }) * * const dfs = Graph.dfs(graph, { startNodes: [0] }) * * // Map to just the node data * const values = Array.from(dfs.visit((index, data) => data)) * console.log(values) // ["A", "B"] * * // Map to custom objects * const custom = Array.from(dfs.visit((index, data) => ({ id: index, name: data }))) * console.log(custom) // [{ id: 0, name: "A" }, { id: 1, name: "B" }] * ``` * * @since 3.18.0 * @category iterators */ visit: (f: (index: T, data: N) => U) => Iterable); } /** * Type alias for node iteration using Walker. * NodeWalker is represented as Walker. * * @since 3.18.0 * @category models */ export type NodeWalker = Walker; /** * Type alias for edge iteration using Walker. * EdgeWalker is represented as Walker>. * * @since 3.18.0 * @category models */ export type EdgeWalker = Walker>; /** * Returns an iterator over the indices in the walker. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * Graph.addEdge(mutable, a, b, 1) * }) * * const dfs = Graph.dfs(graph, { startNodes: [0] }) * const indices = Array.from(Graph.indices(dfs)) * console.log(indices) // [0, 1] * ``` * * @since 3.18.0 * @category utilities */ export declare const indices: (walker: Walker) => Iterable; /** * Returns an iterator over the values (data) in the walker. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * Graph.addEdge(mutable, a, b, 1) * }) * * const dfs = Graph.dfs(graph, { startNodes: [0] }) * const values = Array.from(Graph.values(dfs)) * console.log(values) // ["A", "B"] * ``` * * @since 3.18.0 * @category utilities */ export declare const values: (walker: Walker) => Iterable; /** * Returns an iterator over [index, data] entries in the walker. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * Graph.addEdge(mutable, a, b, 1) * }) * * const dfs = Graph.dfs(graph, { startNodes: [0] }) * const entries = Array.from(Graph.entries(dfs)) * console.log(entries) // [[0, "A"], [1, "B"]] * ``` * * @since 3.18.0 * @category utilities */ export declare const entries: (walker: Walker) => Iterable<[T, N]>; /** * Configuration options for DFS iterator. * * @since 3.18.0 * @category models */ export interface DfsConfig { readonly startNodes?: Array; readonly direction?: Direction; } /** * Creates a new DFS iterator with optional configuration. * * The iterator maintains a stack of nodes to visit and tracks discovered nodes. * It provides lazy evaluation of the depth-first search. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, 1) * Graph.addEdge(mutable, b, c, 1) * }) * * // Start from a specific node * const dfs1 = Graph.dfs(graph, { startNodes: [0] }) * for (const nodeIndex of Graph.indices(dfs1)) { * console.log(nodeIndex) // Traverses in DFS order: 0, 1, 2 * } * * // Empty iterator (no starting nodes) * const dfs2 = Graph.dfs(graph) * // Can be used programmatically * ``` * * @since 3.18.0 * @category iterators */ export declare const dfs: (graph: Graph | MutableGraph, config?: DfsConfig) => NodeWalker; /** * Configuration options for BFS iterator. * * @since 3.18.0 * @category models */ export interface BfsConfig { readonly startNodes?: Array; readonly direction?: Direction; } /** * Creates a new BFS iterator with optional configuration. * * The iterator maintains a queue of nodes to visit and tracks discovered nodes. * It provides lazy evaluation of the breadth-first search. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, 1) * Graph.addEdge(mutable, b, c, 1) * }) * * // Start from a specific node * const bfs1 = Graph.bfs(graph, { startNodes: [0] }) * for (const nodeIndex of Graph.indices(bfs1)) { * console.log(nodeIndex) // Traverses in BFS order: 0, 1, 2 * } * * // Empty iterator (no starting nodes) * const bfs2 = Graph.bfs(graph) * // Can be used programmatically * ``` * * @since 3.18.0 * @category iterators */ export declare const bfs: (graph: Graph | MutableGraph, config?: BfsConfig) => NodeWalker; /** * Configuration options for topological sort iterator. * * @since 3.18.0 * @category models */ export interface TopoConfig { readonly initials?: Array; } /** * Creates a new topological sort iterator with optional configuration. * * The iterator uses Kahn's algorithm to lazily produce nodes in topological order. * Throws an error if the graph contains cycles. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, 1) * Graph.addEdge(mutable, b, c, 1) * }) * * // Standard topological sort * const topo1 = Graph.topo(graph) * for (const nodeIndex of Graph.indices(topo1)) { * console.log(nodeIndex) // 0, 1, 2 (topological order) * } * * // With initial nodes * const topo2 = Graph.topo(graph, { initials: [0] }) * * // Throws error for cyclic graph * const cyclicGraph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * Graph.addEdge(mutable, a, b, 1) * Graph.addEdge(mutable, b, a, 2) // Creates cycle * }) * * try { * Graph.topo(cyclicGraph) // Throws: "Cannot perform topological sort on cyclic graph" * } catch (error) { * console.log((error as Error).message) * } * ``` * * @since 3.18.0 * @category iterators */ export declare const topo: (graph: Graph | MutableGraph, config?: TopoConfig) => NodeWalker; /** * Configuration options for DFS postorder iterator. * * @since 3.18.0 * @category models */ export interface DfsPostOrderConfig { readonly startNodes?: Array; readonly direction?: Direction; } /** * Creates a new DFS postorder iterator with optional configuration. * * The iterator maintains a stack with visit state tracking and emits nodes * in postorder (after all descendants have been processed). Essential for * dependency resolution and tree destruction algorithms. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const root = Graph.addNode(mutable, "root") * const child1 = Graph.addNode(mutable, "child1") * const child2 = Graph.addNode(mutable, "child2") * Graph.addEdge(mutable, root, child1, 1) * Graph.addEdge(mutable, root, child2, 1) * }) * * // Postorder: children before parents * const postOrder = Graph.dfsPostOrder(graph, { startNodes: [0] }) * for (const node of postOrder) { * console.log(node) // 1, 2, 0 * } * ``` * * @since 3.18.0 * @category iterators */ export declare const dfsPostOrder: (graph: Graph | MutableGraph, config?: DfsPostOrderConfig) => NodeWalker; /** * Creates an iterator over all node indices in the graph. * * The iterator produces node indices in the order they were added to the graph. * This provides access to all nodes regardless of connectivity. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, 1) * }) * * const indices = Array.from(Graph.indices(Graph.nodes(graph))) * console.log(indices) // [0, 1, 2] * ``` * * @since 3.18.0 * @category iterators */ export declare const nodes: (graph: Graph | MutableGraph) => NodeWalker; /** * Creates an iterator over all edge indices in the graph. * * The iterator produces edge indices in the order they were added to the graph. * This provides access to all edges regardless of connectivity. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const a = Graph.addNode(mutable, "A") * const b = Graph.addNode(mutable, "B") * const c = Graph.addNode(mutable, "C") * Graph.addEdge(mutable, a, b, 1) * Graph.addEdge(mutable, b, c, 2) * }) * * const indices = Array.from(Graph.indices(Graph.edges(graph))) * console.log(indices) // [0, 1] * ``` * * @since 3.18.0 * @category iterators */ export declare const edges: (graph: Graph | MutableGraph) => EdgeWalker; /** * Configuration for externals iterator. * * @since 3.18.0 * @category models */ export interface ExternalsConfig { readonly direction?: Direction; } /** * Creates an iterator over external nodes (nodes without edges in specified direction). * * External nodes are nodes that have no outgoing edges (direction="outgoing") or * no incoming edges (direction="incoming"). These are useful for finding * sources, sinks, or isolated nodes. * * @example * ```ts * import { Graph } from "effect" * * const graph = Graph.directed((mutable) => { * const source = Graph.addNode(mutable, "source") // 0 - no incoming * const middle = Graph.addNode(mutable, "middle") // 1 - has both * const sink = Graph.addNode(mutable, "sink") // 2 - no outgoing * const isolated = Graph.addNode(mutable, "isolated") // 3 - no edges * * Graph.addEdge(mutable, source, middle, 1) * Graph.addEdge(mutable, middle, sink, 2) * }) * * // Nodes with no outgoing edges (sinks + isolated) * const sinks = Array.from(Graph.indices(Graph.externals(graph, { direction: "outgoing" }))) * console.log(sinks) // [2, 3] * * // Nodes with no incoming edges (sources + isolated) * const sources = Array.from(Graph.indices(Graph.externals(graph, { direction: "incoming" }))) * console.log(sources) // [0, 3] * ``` * * @since 3.18.0 * @category iterators */ export declare const externals: (graph: Graph | MutableGraph, config?: ExternalsConfig) => NodeWalker; //# sourceMappingURL=Graph.d.ts.map