| // Copyright (c) 2018, 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. |
| |
| import 'dart:collection'; |
| |
| /// Returns the shortest path from [start] to [target] given the directed |
| /// edges of a graph provided by [edges]. |
| /// |
| /// If [start] `==` [target], an empty [List] is returned and [edges] is never |
| /// called. |
| /// |
| /// If [equals] is provided, it is used to compare nodes in the graph. If |
| /// [equals] is omitted, the node's own [Object.==] is used instead. |
| /// |
| /// Similarly, if [hashCode] is provided, it is used to produce a hash value |
| /// for nodes to efficiently calculate the return value. If it is omitted, the |
| /// key's own [Object.hashCode] is used. |
| /// |
| /// If you supply one of [equals] or [hashCode], you should generally also to |
| /// supply the other. |
| Iterable<T>? shortestPath<T extends Object>( |
| T start, |
| T target, |
| Iterable<T> Function(T) edges, { |
| bool Function(T, T)? equals, |
| int Function(T)? hashCode, |
| }) => |
| _shortestPaths<T>( |
| start, |
| edges, |
| target: target, |
| equals: equals, |
| hashCode: hashCode, |
| )[target]; |
| |
| /// Returns a [Map] of the shortest paths from [start] to all of the nodes in |
| /// the directed graph defined by [edges]. |
| /// |
| /// All return values will contain the key [start] with an empty [List] value. |
| /// |
| /// [start] and all values returned by [edges] must not be `null`. |
| /// If asserts are enabled, an [AssertionError] is raised if these conditions |
| /// are not met. If asserts are not enabled, violations result in undefined |
| /// behavior. |
| /// |
| /// If [equals] is provided, it is used to compare nodes in the graph. If |
| /// [equals] is omitted, the node's own [Object.==] is used instead. |
| /// |
| /// Similarly, if [hashCode] is provided, it is used to produce a hash value |
| /// for nodes to efficiently calculate the return value. If it is omitted, the |
| /// key's own [Object.hashCode] is used. |
| /// |
| /// If you supply one of [equals] or [hashCode], you should generally also to |
| /// supply the other. |
| Map<T, Iterable<T>> shortestPaths<T extends Object>( |
| T start, |
| Iterable<T> Function(T) edges, { |
| bool Function(T, T)? equals, |
| int Function(T)? hashCode, |
| }) => |
| _shortestPaths<T>( |
| start, |
| edges, |
| equals: equals, |
| hashCode: hashCode, |
| ); |
| |
| Map<T, Iterable<T>> _shortestPaths<T extends Object>( |
| T start, |
| Iterable<T> Function(T) edges, { |
| T? target, |
| bool Function(T, T)? equals, |
| int Function(T)? hashCode, |
| }) { |
| final distances = HashMap<T, _Tail<T>>(equals: equals, hashCode: hashCode); |
| distances[start] = _Tail<T>(); |
| |
| final nonNullEquals = equals ??= _defaultEquals; |
| final isTarget = |
| target == null ? _neverTarget : (T node) => nonNullEquals(node, target); |
| if (isTarget(start)) { |
| return distances; |
| } |
| |
| final toVisit = ListQueue<T>()..add(start); |
| |
| while (toVisit.isNotEmpty) { |
| final current = toVisit.removeFirst(); |
| final currentPath = distances[current]!; |
| |
| for (var edge in edges(current)) { |
| final existingPath = distances[edge]; |
| |
| if (existingPath == null) { |
| distances[edge] = currentPath.append(edge); |
| if (isTarget(edge)) { |
| return distances; |
| } |
| toVisit.add(edge); |
| } |
| } |
| } |
| |
| return distances; |
| } |
| |
| bool _defaultEquals(Object a, Object b) => a == b; |
| bool _neverTarget(Object _) => false; |
| |
| /// An immutable iterable that can efficiently return a copy with a value |
| /// appended. |
| /// |
| /// This implementation has an efficient [length] property. |
| /// |
| /// Note that grabbing an [iterator] for the first time is O(n) in time and |
| /// space because it copies all the values to a new list and uses that |
| /// iterator in order to avoid stack overflows for large paths. This copy is |
| /// cached for subsequent calls. |
| class _Tail<T extends Object> extends Iterable<T> { |
| final T? tail; |
| final _Tail<T>? head; |
| @override |
| final int length; |
| _Tail() |
| : tail = null, |
| head = null, |
| length = 0; |
| _Tail._(this.tail, this.head, this.length); |
| _Tail<T> append(T value) => _Tail._(value, this, length + 1); |
| |
| @override |
| Iterator<T> get iterator => _asIterable.iterator; |
| |
| late final _asIterable = () { |
| _Tail<T>? next = this; |
| final reversed = List.generate(length, (_) { |
| final val = next!.tail; |
| next = next!.head; |
| return val as T; |
| }); |
| return reversed.reversed; |
| }(); |
| } |