|  | // Copyright (c) 2021, the Dart project authors.  Please see the AUTHORS file | 
|  | // for details. All rights reserved. Use of this source code is governed by a | 
|  | // BSD-style license that can be found in the LICENSE file. | 
|  |  | 
|  | /// Implementation of a union-find algorithm. | 
|  | /// | 
|  | /// See https://en.wikipedia.org/wiki/Disjoint-set_data_structure | 
|  |  | 
|  | import 'dart:collection'; | 
|  |  | 
|  | class UnionFindNode<T> { | 
|  | final T value; | 
|  | UnionFindNode<T>? parent; | 
|  |  | 
|  | UnionFindNode(this.value); | 
|  | } | 
|  |  | 
|  | class UnionFind<T> { | 
|  | final bool _useIdentity; | 
|  | final Map<T, UnionFindNode<T>> _nodeMap; | 
|  |  | 
|  | UnionFind({bool useIdentity = false}) | 
|  | : _nodeMap = useIdentity ? new LinkedHashMap.identity() : {}, | 
|  | _useIdentity = useIdentity; | 
|  |  | 
|  | UnionFind<T> clone() { | 
|  | UnionFind<T> newUnionFind = new UnionFind<T>(useIdentity: _useIdentity); | 
|  | Map<UnionFindNode<T>, UnionFindNode<T>> oldToNewMap = {}; | 
|  |  | 
|  | UnionFindNode<T> getNewNode(UnionFindNode<T> oldNode) { | 
|  | UnionFindNode<T> newNode = newUnionFind[oldNode.value]; | 
|  | return oldToNewMap[oldNode] = newNode; | 
|  | } | 
|  |  | 
|  | for (UnionFindNode<T> oldNode in _nodeMap.values) { | 
|  | UnionFindNode<T> newNode = getNewNode(oldNode); | 
|  | if (oldNode.parent != null) { | 
|  | newNode.parent = getNewNode(oldNode.parent!); | 
|  | } | 
|  | } | 
|  | return newUnionFind; | 
|  | } | 
|  |  | 
|  | UnionFindNode<T> operator [](T value) => | 
|  | _nodeMap[value] ??= new UnionFindNode<T>(value); | 
|  |  | 
|  | Iterable<UnionFindNode<T>> get nodes => _nodeMap.values; | 
|  |  | 
|  | Iterable<T> get values => nodes.map((n) => n.value); | 
|  |  | 
|  | UnionFindNode<T> findNode(UnionFindNode<T> node) { | 
|  | if (node.parent != null) { | 
|  | // Perform path compression by updating to the effective target. | 
|  | return node.parent = findNode(node.parent!); | 
|  | } | 
|  | return node; | 
|  | } | 
|  |  | 
|  | void unionOfValues(T a, T b) { | 
|  | unionOfNodes(this[a], this[b]); | 
|  | } | 
|  |  | 
|  | UnionFindNode<T> unionOfNodes(UnionFindNode<T> a, UnionFindNode<T> b) { | 
|  | UnionFindNode<T> rootA = findNode(a); | 
|  | UnionFindNode<T> rootB = findNode(b); | 
|  | if (rootA != rootB) { | 
|  | return rootB.parent = rootA; | 
|  | } | 
|  | return rootA; | 
|  | } | 
|  |  | 
|  | bool valuesInSameSet(T a, T b) { | 
|  | UnionFindNode<T>? node1 = _nodeMap[a]; | 
|  | UnionFindNode<T>? node2 = _nodeMap[b]; | 
|  | return node1 != null && node2 != null && nodesInSameSet(node1, node2); | 
|  | } | 
|  |  | 
|  | bool nodesInSameSet(UnionFindNode<T> a, UnionFindNode<T> b) { | 
|  | return findNode(a) == findNode(b); | 
|  | } | 
|  | } |