package tp;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

import org.graphstream.graph.Edge;
import org.graphstream.graph.Node;

public class FlowUtils {

  /**
   * Gives every subset of a given Set. 2^n complexity.
   * 
   * @param             <T> type of the elements contained in the set
   * @param originalSet the set
   * @return a set of sets
   */
  public static <T> Set<Set<T>> powerSet(Set<T> originalSet) {
    Set<Set<T>> sets = new HashSet<Set<T>>();

    if (originalSet.isEmpty()) {
      sets.add(new HashSet<T>());
      return sets;
    }

    List<T> list = new ArrayList<T>(originalSet);
    T head = list.get(0);
    Set<T> rest = new HashSet<T>(list.subList(1, list.size()));
    for (Set<T> set : powerSet(rest)) {
      Set<T> newSet = new HashSet<T>();
      newSet.add(head);
      newSet.addAll(set);
      sets.add(newSet);
      sets.add(set);
    }

    sets.removeIf(s -> s.isEmpty());

    return sets;
  }

  /**
   * Gives minimum capacity of a set of cuts
   * 
   * @param cuts set of set of edges (cuts)
   * @return a double that represents the minimum of capacities
   */
  public static double minCapacity(Set<Set<Edge>> cuts) {
    return cuts
      .stream()
      .map(s ->
        s
          .stream()
          .map(e -> (Double) e.getAttribute("c"))
          .reduce(0.0, Double::sum)
      )
      .reduce(Double::min)
      .get();
  }

  /**
   * Gives minimum capacity of a set of cuts
   * 
   * @param cuts set of set of edges (cuts)
   * @return a double that represents the minimum of capacities
   */
  public static Set<Edge> minCut(Set<Set<Edge>> cuts) {
    return cuts
      .stream()
      .collect(Collectors.minBy(
        (a, b) ->
        (int)
        (
          a
            .stream()
            .map(e -> (Double) e.getAttribute("c"))
            .reduce(0.0, Double::sum)
          -
          b
            .stream()
            .map(e -> (Double) e.getAttribute("c"))
            .reduce(0.0, Double::sum)
        )
      ))
      .get();
  }

  /**
   * Gives a cut for a given set of nodes
   * 
   * @param nodes the set of nodes
   * @return a set of edges (cut)
   */
  public static Set<Edge> cut(Set<Node> nodes) {
    return nodes
      .stream()
      .map(n ->
        n
          .getLeavingEdgeSet()
          .stream()
          .filter(e -> !nodes.contains(e.getTargetNode()))
      )
      .flatMap(s -> s).collect(Collectors.toSet());
  }
}