@SuppressWarnings("rawtypes")
  private static void removeMergeBeforeFlatten(StreamGraph streamGraph) {
    Set<Entry<Integer, StreamInvokable<?, ?>>> invokables = streamGraph.getInvokables();
    List<Integer> flatteners = new ArrayList<Integer>();

    for (Entry<Integer, StreamInvokable<?, ?>> entry : invokables) {
      if (entry.getValue() instanceof WindowFlattener) {
        flatteners.add(entry.getKey());
      }
    }

    for (Integer flattener : flatteners) {
      // Flatteners should have exactly one input
      Integer input = streamGraph.getInEdges(flattener).get(0);

      // Check whether the flatten is applied after a merge
      if (streamGraph.getInvokable(input) instanceof WindowMerger) {

        // Mergers should have exactly one input
        Integer mergeInput = streamGraph.getInEdges(input).get(0);
        streamGraph.setEdge(
            mergeInput, flattener, new DistributePartitioner(true), 0, new ArrayList<String>());

        // If the merger is only connected to the flattener we delete it
        // completely, otherwise we only remove the edge
        if (streamGraph.getOutEdges(input).size() > 1) {
          streamGraph.removeEdge(input, flattener);
        } else {
          streamGraph.removeVertex(input);
        }

        streamGraph.setParallelism(flattener, streamGraph.getParallelism(mergeInput));
      }
    }
  }
  private static void setDiscretizerReuse(StreamGraph streamGraph) {

    Set<Entry<Integer, StreamInvokable<?, ?>>> invokables = streamGraph.getInvokables();
    List<Tuple2<Integer, StreamDiscretizer<?>>> discretizers =
        new ArrayList<Tuple2<Integer, StreamDiscretizer<?>>>();

    // Get the discretizers
    for (Entry<Integer, StreamInvokable<?, ?>> entry : invokables) {
      if (entry.getValue() instanceof StreamDiscretizer) {
        discretizers.add(
            new Tuple2<Integer, StreamDiscretizer<?>>(
                entry.getKey(), (StreamDiscretizer<?>) entry.getValue()));
      }
    }

    List<Tuple2<StreamDiscretizer<?>, List<Integer>>> matchingDiscretizers =
        new ArrayList<Tuple2<StreamDiscretizer<?>, List<Integer>>>();

    for (Tuple2<Integer, StreamDiscretizer<?>> discretizer : discretizers) {
      boolean inMatching = false;
      for (Tuple2<StreamDiscretizer<?>, List<Integer>> matching : matchingDiscretizers) {
        Set<Integer> discretizerInEdges =
            new HashSet<Integer>(streamGraph.getInEdges(discretizer.f0));
        Set<Integer> matchingInEdges =
            new HashSet<Integer>(streamGraph.getInEdges(matching.f1.get(0)));

        if (discretizer.f1.equals(matching.f0) && discretizerInEdges.equals(matchingInEdges)) {
          matching.f1.add(discretizer.f0);
          inMatching = true;
          break;
        }
      }
      if (!inMatching) {
        List<Integer> matchingNames = new ArrayList<Integer>();
        matchingNames.add(discretizer.f0);
        matchingDiscretizers.add(
            new Tuple2<StreamDiscretizer<?>, List<Integer>>(discretizer.f1, matchingNames));
      }
    }

    for (Tuple2<StreamDiscretizer<?>, List<Integer>> matching : matchingDiscretizers) {
      List<Integer> matchList = matching.f1;
      if (matchList.size() > 1) {
        Integer first = matchList.get(0);
        for (int i = 1; i < matchList.size(); i++) {
          replaceDiscretizer(streamGraph, matchList.get(i), first);
        }
      }
    }
  }
  private static void replaceDiscretizer(
      StreamGraph streamGraph, Integer toReplace, Integer replaceWith) {
    // Convert to array to create a copy
    List<Integer> outEdges = new ArrayList<Integer>(streamGraph.getOutEdges(toReplace));

    int numOutputs = outEdges.size();

    // Reconnect outputs
    for (int i = 0; i < numOutputs; i++) {
      Integer output = outEdges.get(i);

      streamGraph.setEdge(
          replaceWith,
          output,
          streamGraph.getOutPartitioner(toReplace, output),
          0,
          new ArrayList<String>());
      streamGraph.removeEdge(toReplace, output);
    }

    List<Integer> inEdges = new ArrayList<Integer>(streamGraph.getInEdges(toReplace));
    // Remove inputs
    for (Integer input : inEdges) {
      streamGraph.removeEdge(input, toReplace);
    }

    streamGraph.removeVertex(toReplace);
  }