// Path: mug/src/main/java/com/google/mu/util/stream/MoreCollectors.java
// public static <T> Collector<T, ?, List<T>> toListAndThen(Consumer<? super List<T>> arranger) {
//   requireNonNull(arranger);
//   Collector<T, ?, List<T>> rejectingNulls =
//       Collectors.mapping(Objects::requireNonNull, Collectors.toCollection(ArrayList::new));
//   return Collectors.collectingAndThen(rejectingNulls, list -> {
//     arranger.accept(list);
//     return Collections.unmodifiableList(list);
//   });
// }
// 
// Path: mug/src/main/java/com/google/mu/util/stream/MoreStreams.java
// public static <T> Stream<T> whileNotNull(Supplier<? extends T> supplier) {
//   requireNonNull(supplier);
//   return StreamSupport.stream(
//       new Spliterator<T>() {
//         @Override public boolean tryAdvance(Consumer<? super T> action) {
//           T generated = supplier.get();
//           if (generated == null) {
//             return false;
//           }
//           action.accept(generated);
//           return true;
//         }
//         @Override public int characteristics() {
//           return Spliterator.NONNULL | Spliterator.ORDERED;
//         }
//         @Override public long estimateSize() {
//           return Long.MAX_VALUE;
//         }
//         @Override public Spliterator<T> trySplit() {
//           return null;
//         }
//       },
//       false);
// }
// 
// Path: mug/src/main/java/com/google/mu/util/stream/MoreStreams.java
// public static <T> Stream<T> withSideEffect(Stream<T> stream, Consumer<? super T> sideEffect) {
//   requireNonNull(stream);
//   requireNonNull(sideEffect);
//   return StreamSupport.stream(() -> withSideEffect(stream.spliterator(), sideEffect), 0, false);
// }


import static com.google.mu.util.stream.MoreCollectors.toListAndThen;
import static com.google.mu.util.stream.MoreStreams.whileNotNull;
import static com.google.mu.util.stream.MoreStreams.withSideEffect;
import static java.util.Objects.requireNonNull;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;
      });
    }

    private Stream<N> topDown(InsertionOrder order) {
      return whileNotNull(() -> {
        do {
          if (visitNext()) {
            N next = visited;
            Stream<? extends N> successors = findSuccessors.apply(next);
            if (successors != null) order.insertInto(horizon, successors.spliterator());
            return next;
          }
        } while (!horizon.isEmpty());
        return null; // no more element
      });
    }

    private boolean visitNext() {
      Spliterator<? extends N> top = horizon.getFirst();
      while (top.tryAdvance(this)) {
        if (tracker.test(visited)) return true;
      }
      horizon.removeFirst();
      return false;
    }

    List<N> topologicalOrder(Iterable<? extends N> startNodes) {
      CycleTracker cycleDetector = new CycleTracker();
      return cycleDetector.startPostOrder(startNodes, n -> {
        throw new CyclicGraphException(
// Path: mug/src/main/java/com/google/mu/util/stream/MoreCollectors.java
// public static <T> Collector<T, ?, List<T>> toListAndThen(Consumer<? super List<T>> arranger) {
//   requireNonNull(arranger);
//   Collector<T, ?, List<T>> rejectingNulls =
//       Collectors.mapping(Objects::requireNonNull, Collectors.toCollection(ArrayList::new));
//   return Collectors.collectingAndThen(rejectingNulls, list -> {
//     arranger.accept(list);
//     return Collections.unmodifiableList(list);
//   });
// }
// 
// Path: mug/src/main/java/com/google/mu/util/stream/MoreStreams.java
// public static <T> Stream<T> whileNotNull(Supplier<? extends T> supplier) {
//   requireNonNull(supplier);
//   return StreamSupport.stream(
//       new Spliterator<T>() {
//         @Override public boolean tryAdvance(Consumer<? super T> action) {
//           T generated = supplier.get();
//           if (generated == null) {
//             return false;
//           }
//           action.accept(generated);
//           return true;
//         }
//         @Override public int characteristics() {
//           return Spliterator.NONNULL | Spliterator.ORDERED;
//         }
//         @Override public long estimateSize() {
//           return Long.MAX_VALUE;
//         }
//         @Override public Spliterator<T> trySplit() {
//           return null;
//         }
//       },
//       false);
// }
// 
// Path: mug/src/main/java/com/google/mu/util/stream/MoreStreams.java
// public static <T> Stream<T> withSideEffect(Stream<T> stream, Consumer<? super T> sideEffect) {
//   requireNonNull(stream);
//   requireNonNull(sideEffect);
//   return StreamSupport.stream(() -> withSideEffect(stream.spliterator(), sideEffect), 0, false);
// }


// Path: mug/src/main/java/com/google/mu/util/graph/GraphWalker.java
import static com.google.mu.util.stream.MoreCollectors.toListAndThen;
import static com.google.mu.util.stream.MoreStreams.whileNotNull;
import static com.google.mu.util.stream.MoreStreams.withSideEffect;
import static java.util.Objects.requireNonNull;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Deque;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Queue;
import java.util.Spliterator;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Stream;

      });
    }

    private Stream<N> topDown(InsertionOrder order) {
      return whileNotNull(() -> {
        do {
          if (visitNext()) {
            N next = visited;
            Stream<? extends N> successors = findSuccessors.apply(next);
            if (successors != null) order.insertInto(horizon, successors.spliterator());
            return next;
          }
        } while (!horizon.isEmpty());
        return null; // no more element
      });
    }

    private boolean visitNext() {
      Spliterator<? extends N> top = horizon.getFirst();
      while (top.tryAdvance(this)) {
        if (tracker.test(visited)) return true;
      }
      horizon.removeFirst();
      return false;
    }

    List<N> topologicalOrder(Iterable<? extends N> startNodes) {
      CycleTracker cycleDetector = new CycleTracker();
      return cycleDetector.startPostOrder(startNodes, n -> {
        throw new CyclicGraphException(
            cycleDetector.currentPath().collect(toListAndThen(l -> l.add(n))));