// 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 final Deque<Spliterator<? extends N>> horizon = new ArrayDeque<>();
    private N visited;

    Walk(
        Function<? super N, ? extends Stream<? extends N>> findSuccessors,
        Predicate<? super N> tracker) {
      this.findSuccessors = findSuccessors;
      this.tracker = tracker;
    }

    @Override public void accept(N value) {
      this.visited = requireNonNull(value);
    }

    Stream<N> breadthFirst(Iterable<? extends N> startNodes) {
      horizon.add(startNodes.spliterator());
      return topDown(Queue::add);
    }

    Stream<N> preOrder(Iterable<? extends N> startNodes) {
      horizon.push(startNodes.spliterator());
      return topDown(Deque::push);
    }

    Stream<N> postOrder(Iterable<? extends N> startNodes) {
      return postOrder(startNodes, new ArrayDeque<>());
    }

    private Stream<N> postOrder(Iterable<? extends N> startNodes, Deque<N> roots) {
      horizon.push(startNodes.spliterator());
// 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 final Deque<Spliterator<? extends N>> horizon = new ArrayDeque<>();
    private N visited;

    Walk(
        Function<? super N, ? extends Stream<? extends N>> findSuccessors,
        Predicate<? super N> tracker) {
      this.findSuccessors = findSuccessors;
      this.tracker = tracker;
    }

    @Override public void accept(N value) {
      this.visited = requireNonNull(value);
    }

    Stream<N> breadthFirst(Iterable<? extends N> startNodes) {
      horizon.add(startNodes.spliterator());
      return topDown(Queue::add);
    }

    Stream<N> preOrder(Iterable<? extends N> startNodes) {
      horizon.push(startNodes.spliterator());
      return topDown(Deque::push);
    }

    Stream<N> postOrder(Iterable<? extends N> startNodes) {
      return postOrder(startNodes, new ArrayDeque<>());
    }

    private Stream<N> postOrder(Iterable<? extends N> startNodes, Deque<N> roots) {
      horizon.push(startNodes.spliterator());
      return whileNotNull(() -> {