// Path: src/main/java/com/pengyifan/commons/collections/ImmutableCollectors.java
// public final class ImmutableCollectors {
// 
//   private ImmutableCollectors() throws InstantiationException {
//     throw new InstantiationException("This class is not for instantiation");
//   }
// 
//   /**
//    * Returns a {@link Collector} that accumulates the input elements into a new
//    * {@link ImmutableList}.
//    *
//    * @param <T> the type of the input elements
//    * @return a {@link Collector} which collects all the input elements into a
//    * {@link ImmutableList}, in encounter order
//    */
//   public static <T> Collector<T, ?, ImmutableList<T>> toList() {
//     return Collector.of(
//         ImmutableList.Builder::new,
//         ImmutableList.Builder::add,
//         (left, right) -> left.addAll(right.build()),
//         (Function<ImmutableList.Builder<T>, ImmutableList<T>>) ImmutableList.Builder::build);
//   }
// 
//   /**
//    * Returns a {@link Collector} that accumulates the input elements into a new
//    * {@link ImmutableSet}.
//    *
//    * @param <T> the type of the input elements
//    * @return a {@link Collector} which collects all the input elements into a
//    * {@link ImmutableSet}
//    */
//   public static <T> Collector<T, ?, ImmutableSet<T>> toSet() {
//     return Collector.of(
//         ImmutableSet.Builder::new,
//         ImmutableSet.Builder::add,
//         (left, right) -> left.addAll(right.build()),
//         (Function<ImmutableSet.Builder<T>, ImmutableSet<T>>) ImmutableSet.Builder::build,
//         Collector.Characteristics.UNORDERED);
//   }
// 
//   /**
//    * Returns a {@link Collector} that accumulates elements into a
//    * {@code ImmutableMap} whose keys and values are the result of applying the
//    * provided mapping functions to the input elements.
//    * <p>
//    * If the mapped keys contains duplicates (according to
//    * {@link Object#equals(Object)}), the last element will be stored.
//    *
//    * @param <T>         the type of the input elements
//    * @param <K>         the output type of the key mapping function
//    * @param <V>         the output type of the value mapping function
//    * @param keyMapper   a mapping function to produce keys
//    * @param valueMapper a mapping function to produce values
//    * @return a {@link Collector} which collects elements into a
//    * {@link ImmutableMap} whose keys and values are the result of
//    * applying mapping functions to the input elements
//    */
//   public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toMap(
//       Function<? super T, ? extends K> keyMapper,
//       Function<? super T, ? extends V> valueMapper) {
// 
//     BiConsumer<ImmutableMap.Builder<K, V>, T> accumulator =
//         (map, element) -> map.put(keyMapper.apply(element),
//             valueMapper.apply(element));
// 
//     return Collector.of(
//         ImmutableMap.Builder::new,
//         accumulator,
//         (left, right) -> left.putAll(right.build()),
//         ImmutableMap.Builder::build);
//   }
// 
// }


import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.pengyifan.commons.collections.ImmutableCollectors;
import edu.stanford.nlp.graph.ConnectedComponents;
import edu.stanford.nlp.graph.Graph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.javatuples.Pair;
import org.jgrapht.DirectedGraph;
import org.jgrapht.EdgeFactory;
import org.jgrapht.graph.AbstractBaseGraph;
import org.jgrapht.graph.ClassBasedEdgeFactory;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;
  }

  @Override
  public Set<V> getParents(V vertex) {
    return incomingEdgesOf(vertex).stream()
        .map(this::getEdgeSource)
        .collect(ImmutableCollectors.toSet());
  }

  public ImmutableSet<V> getParents(@Nonnull V v, @Nonnull Predicate<E> edgePredicate) {
    return incomingEdgesOf(v).stream()
        .filter(edgePredicate)
        .map(this::getEdgeSource)
        .collect(ImmutableCollectors.toSet());
  }

  public boolean hasIncomingEdgesOf(@Nonnull V v, @Nonnull Predicate<E> edgePredicate) {
    return incomingEdgesOf(v).stream().filter(edgePredicate).findAny().isPresent();
  }

  public boolean hasOutgoingEdgesOf(@Nonnull V v, @Nonnull Predicate<E> edgePredicate) {
    return outgoingEdgesOf(v).stream().filter(edgePredicate).findAny().isPresent();
  }

  @Override
  public int inDegreeOf(V vertex) {
    return incomingEdgesOf(vertex).size();
  }

  public ImmutableSet<E> incomingEdgesOf(@Nonnull V v, @Nonnull Predicate<E> edgePredicate) {
// Path: src/main/java/com/pengyifan/commons/collections/ImmutableCollectors.java
// public final class ImmutableCollectors {
// 
//   private ImmutableCollectors() throws InstantiationException {
//     throw new InstantiationException("This class is not for instantiation");
//   }
// 
//   /**
//    * Returns a {@link Collector} that accumulates the input elements into a new
//    * {@link ImmutableList}.
//    *
//    * @param <T> the type of the input elements
//    * @return a {@link Collector} which collects all the input elements into a
//    * {@link ImmutableList}, in encounter order
//    */
//   public static <T> Collector<T, ?, ImmutableList<T>> toList() {
//     return Collector.of(
//         ImmutableList.Builder::new,
//         ImmutableList.Builder::add,
//         (left, right) -> left.addAll(right.build()),
//         (Function<ImmutableList.Builder<T>, ImmutableList<T>>) ImmutableList.Builder::build);
//   }
// 
//   /**
//    * Returns a {@link Collector} that accumulates the input elements into a new
//    * {@link ImmutableSet}.
//    *
//    * @param <T> the type of the input elements
//    * @return a {@link Collector} which collects all the input elements into a
//    * {@link ImmutableSet}
//    */
//   public static <T> Collector<T, ?, ImmutableSet<T>> toSet() {
//     return Collector.of(
//         ImmutableSet.Builder::new,
//         ImmutableSet.Builder::add,
//         (left, right) -> left.addAll(right.build()),
//         (Function<ImmutableSet.Builder<T>, ImmutableSet<T>>) ImmutableSet.Builder::build,
//         Collector.Characteristics.UNORDERED);
//   }
// 
//   /**
//    * Returns a {@link Collector} that accumulates elements into a
//    * {@code ImmutableMap} whose keys and values are the result of applying the
//    * provided mapping functions to the input elements.
//    * <p>
//    * If the mapped keys contains duplicates (according to
//    * {@link Object#equals(Object)}), the last element will be stored.
//    *
//    * @param <T>         the type of the input elements
//    * @param <K>         the output type of the key mapping function
//    * @param <V>         the output type of the value mapping function
//    * @param keyMapper   a mapping function to produce keys
//    * @param valueMapper a mapping function to produce values
//    * @return a {@link Collector} which collects elements into a
//    * {@link ImmutableMap} whose keys and values are the result of
//    * applying mapping functions to the input elements
//    */
//   public static <T, K, V> Collector<T, ?, ImmutableMap<K, V>> toMap(
//       Function<? super T, ? extends K> keyMapper,
//       Function<? super T, ? extends V> valueMapper) {
// 
//     BiConsumer<ImmutableMap.Builder<K, V>, T> accumulator =
//         (map, element) -> map.put(keyMapper.apply(element),
//             valueMapper.apply(element));
// 
//     return Collector.of(
//         ImmutableMap.Builder::new,
//         accumulator,
//         (left, right) -> left.putAll(right.build()),
//         ImmutableMap.Builder::build);
//   }
// 
// }


// Path: src/main/java/com/pengyifan/commons/collections/graph/DirectedLoopGraph.java
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Sets;
import com.pengyifan.commons.collections.ImmutableCollectors;
import edu.stanford.nlp.graph.ConnectedComponents;
import edu.stanford.nlp.graph.Graph;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.StringJoiner;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import javax.annotation.Nonnull;
import org.javatuples.Pair;
import org.jgrapht.DirectedGraph;
import org.jgrapht.EdgeFactory;
import org.jgrapht.graph.AbstractBaseGraph;
import org.jgrapht.graph.ClassBasedEdgeFactory;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkState;

  }

  @Override
  public Set<V> getParents(V vertex) {
    return incomingEdgesOf(vertex).stream()
        .map(this::getEdgeSource)
        .collect(ImmutableCollectors.toSet());
  }

  public ImmutableSet<V> getParents(@Nonnull V v, @Nonnull Predicate<E> edgePredicate) {
    return incomingEdgesOf(v).stream()
        .filter(edgePredicate)
        .map(this::getEdgeSource)
        .collect(ImmutableCollectors.toSet());
  }

  public boolean hasIncomingEdgesOf(@Nonnull V v, @Nonnull Predicate<E> edgePredicate) {
    return incomingEdgesOf(v).stream().filter(edgePredicate).findAny().isPresent();
  }

  public boolean hasOutgoingEdgesOf(@Nonnull V v, @Nonnull Predicate<E> edgePredicate) {
    return outgoingEdgesOf(v).stream().filter(edgePredicate).findAny().isPresent();
  }

  @Override
  public int inDegreeOf(V vertex) {
    return incomingEdgesOf(vertex).size();
  }

  public ImmutableSet<E> incomingEdgesOf(@Nonnull V v, @Nonnull Predicate<E> edgePredicate) {
    return incomingEdgesOf(v).stream().filter(edgePredicate).collect(ImmutableCollectors.toSet());