private boolean nextIsLeft() {
   boolean returnLeft;
   if (leftHasNext) {
     if (rightHasNext) {
       int leftFirstDayIndex = nextLeft.getFirstDayIndex();
       int rightFirstDayIndex = nextRight.getFirstDayIndex();
       returnLeft = leftFirstDayIndex <= rightFirstDayIndex;
     } else {
       returnLeft = true;
     }
   } else {
     if (rightHasNext) {
       returnLeft = false;
     } else {
       throw new NoSuchElementException();
     }
   }
   return returnLeft;
 }
 public boolean hasNextWithMaximumFirstDayIndexes(
     int leftMinimumFirstDayIndex, int rightMinimumFirstDayIndex) {
   if (!hasNext()) {
     return false;
   }
   boolean nextIsLeft = nextIsLeft();
   if (nextIsLeft) {
     int firstDayIndex = nextLeft.getFirstDayIndex();
     // It should not be conflict in the same pillar and it should be in conflict with the other
     // pillar
     return firstDayIndex > leftMinimumFirstDayIndex
         && firstDayIndex <= rightMinimumFirstDayIndex;
   } else {
     int firstDayIndex = nextRight.getFirstDayIndex();
     // It should not be conflict in the same pillar and it should be in conflict with the other
     // pillar
     return firstDayIndex > rightMinimumFirstDayIndex
         && firstDayIndex <= leftMinimumFirstDayIndex;
   }
 }
  public List<Move> createMoveList(NurseRoster nurseRoster) {
    List<Employee> employeeList = nurseRoster.getEmployeeList();
    // This code assumes the shiftAssignmentList is sorted
    // Filter out every immovable ShiftAssignment
    List<ShiftAssignment> shiftAssignmentList =
        new ArrayList<ShiftAssignment>(nurseRoster.getShiftAssignmentList());
    for (Iterator<ShiftAssignment> it = shiftAssignmentList.iterator(); it.hasNext(); ) {
      ShiftAssignment shiftAssignment = it.next();
      if (!filter.accept(nurseRoster, shiftAssignment)) {
        it.remove();
      }
    }

    // Hash the assignments per employee
    Map<Employee, List<AssignmentSequence>> employeeToAssignmentSequenceListMap =
        new HashMap<Employee, List<AssignmentSequence>>(employeeList.size());
    int assignmentSequenceCapacity = nurseRoster.getShiftDateList().size() + 1 / 2;
    for (Employee employee : employeeList) {
      employeeToAssignmentSequenceListMap.put(
          employee, new ArrayList<AssignmentSequence>(assignmentSequenceCapacity));
    }
    for (ShiftAssignment shiftAssignment : shiftAssignmentList) {
      Employee employee = shiftAssignment.getEmployee();
      List<AssignmentSequence> assignmentSequenceList =
          employeeToAssignmentSequenceListMap.get(employee);
      if (assignmentSequenceList.isEmpty()) {
        AssignmentSequence assignmentSequence = new AssignmentSequence(shiftAssignment);
        assignmentSequenceList.add(assignmentSequence);
      } else {
        AssignmentSequence lastAssignmentSequence =
            assignmentSequenceList // getLast()
                .get(assignmentSequenceList.size() - 1);
        if (lastAssignmentSequence.belongsHere(shiftAssignment)) {
          lastAssignmentSequence.add(shiftAssignment);
        } else {
          AssignmentSequence assignmentSequence = new AssignmentSequence(shiftAssignment);
          assignmentSequenceList.add(assignmentSequence);
        }
      }
    }

    // The create the move list
    List<Move> moveList = new ArrayList<Move>();
    // For every 2 distinct employees
    for (ListIterator<Employee> leftEmployeeIt = employeeList.listIterator();
        leftEmployeeIt.hasNext(); ) {
      Employee leftEmployee = leftEmployeeIt.next();
      List<AssignmentSequence> leftAssignmentSequenceList =
          employeeToAssignmentSequenceListMap.get(leftEmployee);
      for (ListIterator<Employee> rightEmployeeIt =
              employeeList.listIterator(leftEmployeeIt.nextIndex());
          rightEmployeeIt.hasNext(); ) {
        Employee rightEmployee = rightEmployeeIt.next();
        List<AssignmentSequence> rightAssignmentSequenceList =
            employeeToAssignmentSequenceListMap.get(rightEmployee);

        final int SWITCH_LENGTH = 3;
        for (AssignmentSequence leftAssignmentSequence : leftAssignmentSequenceList) {
          List<ShiftAssignment> leftShiftAssignmentList =
              leftAssignmentSequence.getShiftAssignmentList();
          for (int leftIndex = 0;
              leftIndex <= leftShiftAssignmentList.size() - SWITCH_LENGTH;
              leftIndex++) {

            for (AssignmentSequence rightAssignmentSequence : rightAssignmentSequenceList) {
              List<ShiftAssignment> rightShiftAssignmentList =
                  rightAssignmentSequence.getShiftAssignmentList();
              for (int rightIndex = 0;
                  rightIndex <= rightShiftAssignmentList.size() - SWITCH_LENGTH;
                  rightIndex++) {

                List<Move> subMoveList = new ArrayList<Move>(SWITCH_LENGTH * 2);
                for (ShiftAssignment leftShiftAssignment :
                    leftShiftAssignmentList.subList(leftIndex, leftIndex + SWITCH_LENGTH)) {
                  subMoveList.add(new EmployeeChangeMove(leftShiftAssignment, rightEmployee));
                }
                for (ShiftAssignment rightShiftAssignment :
                    rightShiftAssignmentList.subList(rightIndex, rightIndex + SWITCH_LENGTH)) {
                  subMoveList.add(new EmployeeChangeMove(rightShiftAssignment, leftEmployee));
                }
                moveList.add(CompositeMove.buildMove(subMoveList));
              }
            }
          }
        }
      }
    }
    return moveList;
  }
  @Override
  public List<Move> createMoveList(TaRoster taRoster) {
    List<Ta> taList = taRoster.getTaList();
    // This code assumes the courseAssignmentList is sorted
    // Filter out every immovable CourseAssignment
    List<CourseAssignment> courseAssignmentList =
        new ArrayList<>(taRoster.getCourseAssignmentList());

    // Hash the assignments per ta
    Map<Ta, List<AssignmentSequence>> taToAssignmentSequenceListMap = new HashMap<>(taList.size());
    int assignmentSequenceCapacity = taRoster.getCourseDayList().size() + 1 / 2;
    for (Ta ta : taList) {
      taToAssignmentSequenceListMap.put(
          ta, new ArrayList<AssignmentSequence>(assignmentSequenceCapacity));
    }
    for (CourseAssignment courseAssignment : courseAssignmentList) {
      Ta ta = courseAssignment.getTa();
      List<AssignmentSequence> assignmentSequenceList = taToAssignmentSequenceListMap.get(ta);
      if (assignmentSequenceList.isEmpty()) {
        AssignmentSequence assignmentSequence = new AssignmentSequence(courseAssignment);
        assignmentSequenceList.add(assignmentSequence);
      } else {
        AssignmentSequence lastAssignmentSequence =
            assignmentSequenceList // getLast()
                .get(assignmentSequenceList.size() - 1);
        if (lastAssignmentSequence.belongsHere(courseAssignment)) {
          lastAssignmentSequence.add(courseAssignment);
        } else {
          AssignmentSequence assignmentSequence = new AssignmentSequence(courseAssignment);
          assignmentSequenceList.add(assignmentSequence);
        }
      }
    }

    // The create the move list
    List<Move> moveList = new ArrayList<>();
    // For every 2 distinct tas
    for (ListIterator<Ta> leftTaIt = taList.listIterator(); leftTaIt.hasNext(); ) {
      Ta leftTa = leftTaIt.next();
      List<AssignmentSequence> leftAssignmentSequenceList =
          taToAssignmentSequenceListMap.get(leftTa);
      for (ListIterator<Ta> rightTaIt = taList.listIterator(leftTaIt.nextIndex());
          rightTaIt.hasNext(); ) {
        Ta rightTa = rightTaIt.next();
        List<AssignmentSequence> rightAssignmentSequenceList =
            taToAssignmentSequenceListMap.get(rightTa);

        final int SWITCH_LENGTH = 3;
        for (AssignmentSequence leftAssignmentSequence : leftAssignmentSequenceList) {
          List<CourseAssignment> leftCourseAssignmentList =
              leftAssignmentSequence.getCourseAssignmentList();
          for (int leftIndex = 0;
              leftIndex <= leftCourseAssignmentList.size() - SWITCH_LENGTH;
              leftIndex++) {

            for (AssignmentSequence rightAssignmentSequence : rightAssignmentSequenceList) {
              List<CourseAssignment> rightCourseAssignmentList =
                  rightAssignmentSequence.getCourseAssignmentList();
              for (int rightIndex = 0;
                  rightIndex <= rightCourseAssignmentList.size() - SWITCH_LENGTH;
                  rightIndex++) {

                List<Move> subMoveList = new ArrayList<>(SWITCH_LENGTH * 2);
                for (CourseAssignment leftCourseAssignment :
                    leftCourseAssignmentList.subList(leftIndex, leftIndex + SWITCH_LENGTH)) {
                  subMoveList.add(new TaChangeMove(leftCourseAssignment, rightTa));
                }
                for (CourseAssignment rightCourseAssignment :
                    rightCourseAssignmentList.subList(rightIndex, rightIndex + SWITCH_LENGTH)) {
                  subMoveList.add(new TaChangeMove(rightCourseAssignment, leftTa));
                }
                moveList.add(CompositeMove.buildMove(subMoveList));
              }
            }
          }
        }
      }
    }
    return moveList;
  }
  public List<Move> createMoveList(Solution solution) {
    NurseRoster nurseRoster = (NurseRoster) solution;
    List<Employee> employeeList = nurseRoster.getEmployeeList();
    // This code assumes the shiftAssignmentList is sorted
    List<ShiftAssignment> shiftAssignmentList = nurseRoster.getShiftAssignmentList();

    // Hash the assignments per employee
    Map<Employee, List<AssignmentSequence>> employeeToAssignmentSequenceListMap =
        new HashMap<Employee, List<AssignmentSequence>>(employeeList.size());
    int assignmentSequenceCapacity = nurseRoster.getShiftDateList().size() + 1 / 2;
    for (Employee employee : employeeList) {
      employeeToAssignmentSequenceListMap.put(
          employee, new ArrayList<AssignmentSequence>(assignmentSequenceCapacity));
    }
    for (ShiftAssignment shiftAssignment : shiftAssignmentList) {
      Employee employee = shiftAssignment.getEmployee();
      List<AssignmentSequence> assignmentSequenceList =
          employeeToAssignmentSequenceListMap.get(employee);
      if (assignmentSequenceList.isEmpty()) {
        AssignmentSequence assignmentSequence = new AssignmentSequence(employee, shiftAssignment);
        assignmentSequenceList.add(assignmentSequence);
      } else {
        AssignmentSequence lastAssignmentSequence =
            assignmentSequenceList // getLast()
                .get(assignmentSequenceList.size() - 1);
        if (lastAssignmentSequence.belongsHere(shiftAssignment)) {
          lastAssignmentSequence.add(shiftAssignment);
        } else {
          AssignmentSequence assignmentSequence = new AssignmentSequence(employee, shiftAssignment);
          assignmentSequenceList.add(assignmentSequence);
        }
      }
    }

    // The create the move list
    List<Move> moveList = new ArrayList<Move>();
    // For every 2 distinct employees
    for (ListIterator<Employee> leftEmployeeIt = employeeList.listIterator();
        leftEmployeeIt.hasNext(); ) {
      Employee leftEmployee = leftEmployeeIt.next();
      List<AssignmentSequence> leftAssignmentSequenceList =
          employeeToAssignmentSequenceListMap.get(leftEmployee);
      for (ListIterator<Employee> rightEmployeeIt =
              employeeList.listIterator(leftEmployeeIt.nextIndex());
          rightEmployeeIt.hasNext(); ) {
        Employee rightEmployee = rightEmployeeIt.next();
        List<AssignmentSequence> rightAssignmentSequenceList =
            employeeToAssignmentSequenceListMap.get(rightEmployee);

        LowestDayIndexAssignmentSequenceIterator lowestIt =
            new LowestDayIndexAssignmentSequenceIterator(
                leftAssignmentSequenceList, rightAssignmentSequenceList);
        // For every pillar part duo
        while (lowestIt.hasNext()) {
          AssignmentSequence pillarPartAssignmentSequence = lowestIt.next();
          // Note: the initialCapacity is probably to high,
          // which is bad for memory, but the opposite is bad for performance (which is worse)
          List<Move> moveListByPillarPartDuo =
              new ArrayList<Move>(
                  leftAssignmentSequenceList.size() + rightAssignmentSequenceList.size());
          int lastDayIndex = pillarPartAssignmentSequence.getLastDayIndex();
          Employee otherEmployee;
          int leftMinimumFirstDayIndex = Integer.MIN_VALUE;
          int rightMinimumFirstDayIndex = Integer.MIN_VALUE;
          if (lowestIt.isLastNextWasLeft()) {
            otherEmployee = rightEmployee;
            leftMinimumFirstDayIndex = lastDayIndex;
          } else {
            otherEmployee = leftEmployee;
            rightMinimumFirstDayIndex = lastDayIndex;
          }
          moveListByPillarPartDuo.add(
              new EmployeeMultipleChangeMove(
                  pillarPartAssignmentSequence.getEmployee(),
                  pillarPartAssignmentSequence.getShiftAssignmentList(),
                  otherEmployee));
          // For every AssignmentSequence in that pillar part duo
          while (lowestIt.hasNextWithMaximumFirstDayIndexes(
              leftMinimumFirstDayIndex, rightMinimumFirstDayIndex)) {
            pillarPartAssignmentSequence = lowestIt.next();
            lastDayIndex = pillarPartAssignmentSequence.getLastDayIndex();
            if (lowestIt.isLastNextWasLeft()) {
              otherEmployee = rightEmployee;
              leftMinimumFirstDayIndex = Math.max(leftMinimumFirstDayIndex, lastDayIndex);
            } else {
              otherEmployee = leftEmployee;
              rightMinimumFirstDayIndex = Math.max(rightMinimumFirstDayIndex, lastDayIndex);
            }
            moveListByPillarPartDuo.add(
                new EmployeeMultipleChangeMove(
                    pillarPartAssignmentSequence.getEmployee(),
                    pillarPartAssignmentSequence.getShiftAssignmentList(),
                    otherEmployee));
          }
          moveList.add(new CompositeMove(moveListByPillarPartDuo));
        }
      }
    }
    return moveList;
  }