private int recombine_partitions() {

    int result = 0;

    int last_cut = -1;
    int curr_cut = 0;

    for (curr_cut = 0; curr_cut < cont.size() - 2; curr_cut++) {
      if (greedyCuts[curr_cut]) {
        break;
      }
    }

    for (int i = curr_cut + 1; i < cont.size() - 2; i++) {

      if (greedyCuts[i]) {

        // last_cut + 1 ... curr_cut
        // curr_cut + 1 ... i

        FusionInfo f_all = getFusionInfo(last_cut + 1, i);

        if (f_all.getCost().getCost() <= f_all.getWorkEstimateNoPenalty()
            && !does_peek(curr_cut + 1)) {

          greedyCuts[curr_cut] = false;
          // System.out.println("recombining adjacent partitions!");
          // System.out.println("recomb: ["+(last_cut+1)+
          //                   ","+curr_cut+","+i+"]");

          curr_cut = i;
          result--;

        } else {

          last_cut = curr_cut;
          curr_cut = i;
        }
      }
    }

    FusionInfo f_all = getFusionInfo(last_cut + 1, cont.size() - 1);

    if (f_all.getCost().getCost() <= f_all.getWorkEstimateNoPenalty() && !does_peek(curr_cut + 1)) {
      greedyCuts[curr_cut] = false;

      // System.out.println("recombining adjacent partitions!");
      // System.out.println("recomb: ["+(last_cut+1)+
      //                   ","+curr_cut+","+(cont.size()-1)+"]");

      result--;
    }

    return result;
  }
  private int numberOfTiles(int from, int to) {

    // System.out.println("Pipeline.numberOfTiles("+from+","+to+")");

    if (from > to) return 0;
    if (from == to) return childConfig(from).numberOfTiles();

    for (int i = from; i <= to; i++) {
      int child_tiles = childConfig(i).numberOfTiles();
      if (child_tiles > 1) {
        int left = numberOfTiles(from, i - 1);
        int right = numberOfTiles(i + 1, to);

        if (i > 0) greedyCuts[i - 1] = true;
        if (i < cont.size() - 1) greedyCuts[i] = true;

        return left + child_tiles + right;
      }
    }

    // all children fit into one tile each

    // first try fusing everything

    FusionInfo f_all = getFusionInfo(from, to);

    // System.out.println("try one tile
    // ["+f_all.getCost().getCost()+","+f_all.getWorkEstimateNoPenalty()+"]");

    if (f_all.getCost().getCost() <= f_all.getWorkEstimateNoPenalty()) {

      // System.out.println("pipeline segnemt fits into one tile!");
      return 1;
    }

    // find multiplicity of filters

    int mult[] = new int[cont.size()];
    mult[from] = 1; // init mult. of first item to 1

    for (int i = from; i < to; i++) {
      int push = childConfig(i).getFusionInfo().getPushInt() * mult[i];
      int pop = childConfig(i + 1).getFusionInfo().getPopInt();

      int common = gcd(push, pop);
      int xtra = pop / common;

      if (xtra > 1) {
        for (int a = from; a <= i; a++) {
          mult[a] = mult[a] * xtra;
        }
      }

      mult[i + 1] = (push * xtra) / pop;
    }

    // find max bandwidth connection

    int max_size = 0;
    int max_loc = 0;

    for (int i = from; i < to; i++) {

      int size = childConfig(i).getFusionInfo().getPushInt() * mult[i];
      if (size > max_size) {
        max_size = size;
        max_loc = i;
      }
    }

    // System.out.println("max_size: "+max_size+" max_loc: "+max_loc);

    int f_start = max_loc;
    int f_end = max_loc + 1;

    FusionInfo _fi = getFusionInfo(f_start, f_end);

    if (_fi.getCost().getCost() > _fi.getWorkEstimateNoPenalty()) {

      // can not fuse f_start and f_end
      // make cut between f_start and f_end

      int left = numberOfTiles(from, f_start);
      int right = numberOfTiles(f_end, to);

      greedyCuts[f_start] = true;

      return left + right;
    }

    // fuse up and down starting with (max_loc, max_loc + 1) while cant fuse

    boolean can_fuse_up = true, can_fuse_down = true;

    for (; ; ) {

      // check if still can fuse up
      if (can_fuse_up) {
        if (f_start == from) {
          can_fuse_up = false;
        } else {
          FusionInfo fi = getFusionInfo(f_start - 1, f_end);
          if (fi.getCost().getCost() > fi.getWorkEstimateNoPenalty()) {
            can_fuse_up = false;
          }
        }
      }

      // check if still can fuse down
      if (can_fuse_down) {
        if (f_end == to) {
          can_fuse_down = false;
        } else {
          FusionInfo fi = getFusionInfo(f_start, f_end + 1);
          if (fi.getCost().getCost() > fi.getWorkEstimateNoPenalty()) {
            can_fuse_down = false;
          }
        }
      }

      if (can_fuse_up && can_fuse_down) {
        f_start -= 1;
      }
      if (can_fuse_up && !can_fuse_down) {
        f_start -= 1;
      }
      if (!can_fuse_up && can_fuse_down) {
        f_end += 1;
      }

      if (!can_fuse_up && !can_fuse_down) {

        // System.out.println("done fusing ["+f_start+","+f_end+"]");

        int left = numberOfTiles(from, f_start - 1);
        int right = numberOfTiles(f_end + 1, to);

        if (f_start > 0) greedyCuts[f_start - 1] = true;
        if (f_end < cont.size() - 1) greedyCuts[f_end] = true;

        return left + right + 1;
      }
    }
  }