@Override
 public int compare(LaneIndex o1i, LaneIndex o2i) {
   Lane o1 = o1i.getLane();
   Lane o2 = o2i.getLane();
   double startTimeO1 = o1.getStartTime();
   double startTimeO2 = o2.getStartTime();
   if (startTimeO1 > startTimeO2) {
     return -1;
   } else if (startTimeO1 < startTimeO2) {
     return 1;
   } else {
     return 0;
   }
 }
  private void combineParallelLanesAnyPathesUsingJoinWhatever(
      SchedulingTask join, Comparator<Lane> throwerComparator) {

    Set<Lane> predecessors = AbstractAlgorithm.getParentLanesOfTaskSetReadOnly(join);

    List<Lane> throwerList = new ArrayList<Lane>(predecessors);
    List<Lane> catcherList = throwerList;
    Collections.sort(throwerList, throwerComparator);

    // shortest thrower first
    for (Lane thrower : throwerList) {
      if (!algorithm.getWorkflow().existsLane(thrower)) {
        continue;
      }

      SchedulingTaskList throwerLastPath = thrower.getUmodLastPath();
      if (throwerLastPath.size() == thrower.getTasksCount()) {
        continue;
      }

      // for (Lane catcher : catcherList) {
      for (int i = catcherList.size() - 1; i >= 0; --i) {
        Lane catcher = catcherList.get(i); // longest catcher first
        if (!algorithm.getWorkflow().existsLane(catcher) || catcher == thrower) {
          continue;
        }

        // thrower catcher combination
        tryMigrateWhatever(thrower, catcher);
        boolean throwerReleased = !algorithm.getWorkflow().existsLane(thrower);
        if (throwerReleased) {
          // next thrower
          break;
        }
      } // catcher for
    } // thrower for
  }
 /**
  * tries to migrate the last path of thrower after catcher
  *
  * @param thrower
  * @param catcher
  * @return true if thrower is released
  */
 private void tryMigrateWhatever(Lane thrower, Lane catcher) {
   boolean continueOnSameLane;
   do { // continue on same lane
     SchedulingTaskList throwerLastPath = thrower.getUmodLastPath();
     AggregationResult result;
     if (thrower.getInstanceSize() == catcher.getInstanceSize()) {
       result =
           lP_Succ_NoGaps_EqualSize_FixedDL
               .tryMigrateThrowerPathAfterCatcher_EqualInstanceSize_NoGaps(
                   thrower, catcher, throwerLastPath);
     } else {
       result =
           lP_Succ_NoGaps_DifferentSize_FixedDL
               .tryMigrateThrowerPathAfterCatcher_DifferentInstanceSize_ThrowerScale_NoGaps(
                   thrower, catcher, throwerLastPath);
     }
     if (result == null) {
       continueOnSameLane = false;
     } else {
       result.reassign();
       continueOnSameLane = algorithm.getWorkflow().existsLane(result.getThrower());
     }
   } while (continueOnSameLane);
 }