// Path: src/main/java/com/github/rinde/opt/localsearch/Insertions.java
// static class InsertionIndexGenerator implements Iterator<IntList> {
//   private final int[] insertionPositions;
//   private final int originalListSize;
//   private final long length;
//   private int index;
// 
//   InsertionIndexGenerator(int numOfInsertions, int listSize, int startIndex) {
//     checkArgument(startIndex <= listSize,
//       "startIndex (%s) must be <= listSize (%s).",
//       startIndex, listSize);
//     insertionPositions = new int[numOfInsertions];
//     for (int i = 0; i < insertionPositions.length; i++) {
//       insertionPositions[i] = startIndex;
//     }
//     originalListSize = listSize;
//     length = multichoose(listSize + 1 - startIndex, numOfInsertions);
//   }
// 
//   @Override
//   public boolean hasNext() {
//     return index < length;
//   }
// 
//   @Override
//   public IntList next() {
//     if (!hasNext()) {
//       throw new NoSuchElementException();
//     }
//     if (index > 0) {
//       for (int i = 0; i < insertionPositions.length; i++) {
//         if (insertionPositions[i] == originalListSize) {
//           insertionPositions[i - 1]++;
//           for (int j = i; j < insertionPositions.length; j++) {
//             insertionPositions[j] = insertionPositions[i - 1];
//           }
//           break;
//         } else if (i == insertionPositions.length - 1) {
//           insertionPositions[i]++;
//         }
//       }
//     }
//     index++;
//     return IntLists.unmodifiable(new IntArrayList(insertionPositions));
//   }
// 
//   @Deprecated
//   @Override
//   public void remove() {
//     throw new UnsupportedOperationException();
//   }
// }


import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Collections2.filter;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newLinkedHashSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import org.apache.commons.math3.random.RandomAdaptor;
import org.apache.commons.math3.random.RandomGenerator;
import com.github.rinde.opt.localsearch.Insertions.InsertionIndexGenerator;
import com.google.auto.value.AutoValue;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.common.collect.Range;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import it.unimi.dsi.fastutil.doubles.DoubleList;
import it.unimi.dsi.fastutil.doubles.DoubleLists;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntLists;
import it.unimi.dsi.fastutil.objects.Object2DoubleLinkedOpenHashMap;
   * @return The swapped schedule if the cost of the new schedule is better
   *         (lower) than the threshold, {@link Optional#absent()} otherwise.
   */
  static <C, T> Optional<Schedule<C, T>> swap(Schedule<C, T> s, Swap<T> swap,
      double threshold,
      Object2DoubleLinkedOpenHashMap<ImmutableList<T>> cache) {

    checkArgument(swap.fromRow() >= 0 && swap.fromRow() < s.routes.size(),
      "fromRow must be >= 0 and < %s, it is %s.", s.routes.size(),
      swap.fromRow());
    checkArgument(swap.toRow() >= 0 && swap.toRow() < s.routes.size(),
      "toRow must be >= 0 and < %s, it is %s.", s.routes.size(), swap.toRow());

    if (swap.fromRow() == swap.toRow()) {
      // 1. swap within same vehicle
      // compute cost of original ordering
      // compute cost of new ordering
      final double originalCost = s.objectiveValues.getDouble(swap.fromRow());
      final ImmutableList<T> newRoute = inListSwap(s.routes.get(swap.fromRow()),
        swap.toIndices(), swap.item());

      final double newCost = computeCost(s, swap.fromRow(), newRoute, cache);
      final double diff = newCost - originalCost;

      if (diff < threshold) {
        // it improves
        final ImmutableList<ImmutableList<T>> newRoutes = replace(s.routes,
          asIntList(swap.fromRow()),
          ImmutableList.of(newRoute));
        final double newObjectiveValue = s.objectiveValue + diff;
// Path: src/main/java/com/github/rinde/opt/localsearch/Insertions.java
// static class InsertionIndexGenerator implements Iterator<IntList> {
//   private final int[] insertionPositions;
//   private final int originalListSize;
//   private final long length;
//   private int index;
// 
//   InsertionIndexGenerator(int numOfInsertions, int listSize, int startIndex) {
//     checkArgument(startIndex <= listSize,
//       "startIndex (%s) must be <= listSize (%s).",
//       startIndex, listSize);
//     insertionPositions = new int[numOfInsertions];
//     for (int i = 0; i < insertionPositions.length; i++) {
//       insertionPositions[i] = startIndex;
//     }
//     originalListSize = listSize;
//     length = multichoose(listSize + 1 - startIndex, numOfInsertions);
//   }
// 
//   @Override
//   public boolean hasNext() {
//     return index < length;
//   }
// 
//   @Override
//   public IntList next() {
//     if (!hasNext()) {
//       throw new NoSuchElementException();
//     }
//     if (index > 0) {
//       for (int i = 0; i < insertionPositions.length; i++) {
//         if (insertionPositions[i] == originalListSize) {
//           insertionPositions[i - 1]++;
//           for (int j = i; j < insertionPositions.length; j++) {
//             insertionPositions[j] = insertionPositions[i - 1];
//           }
//           break;
//         } else if (i == insertionPositions.length - 1) {
//           insertionPositions[i]++;
//         }
//       }
//     }
//     index++;
//     return IntLists.unmodifiable(new IntArrayList(insertionPositions));
//   }
// 
//   @Deprecated
//   @Override
//   public void remove() {
//     throw new UnsupportedOperationException();
//   }
// }


// Path: src/main/java/com/github/rinde/opt/localsearch/Swaps.java
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
import static com.google.common.base.Predicates.equalTo;
import static com.google.common.base.Predicates.not;
import static com.google.common.collect.Collections2.filter;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newLinkedHashSet;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Set;
import javax.annotation.Nullable;
import org.apache.commons.math3.random.RandomAdaptor;
import org.apache.commons.math3.random.RandomGenerator;
import com.github.rinde.opt.localsearch.Insertions.InsertionIndexGenerator;
import com.google.auto.value.AutoValue;
import com.google.common.base.Function;
import com.google.common.base.Optional;
import com.google.common.base.Predicates;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.Iterators;
import com.google.common.collect.Range;
import it.unimi.dsi.fastutil.doubles.DoubleArrayList;
import it.unimi.dsi.fastutil.doubles.DoubleList;
import it.unimi.dsi.fastutil.doubles.DoubleLists;
import it.unimi.dsi.fastutil.ints.IntArrayList;
import it.unimi.dsi.fastutil.ints.IntList;
import it.unimi.dsi.fastutil.ints.IntLists;
import it.unimi.dsi.fastutil.objects.Object2DoubleLinkedOpenHashMap;

   * @return The swapped schedule if the cost of the new schedule is better
   *         (lower) than the threshold, {@link Optional#absent()} otherwise.
   */
  static <C, T> Optional<Schedule<C, T>> swap(Schedule<C, T> s, Swap<T> swap,
      double threshold,
      Object2DoubleLinkedOpenHashMap<ImmutableList<T>> cache) {

    checkArgument(swap.fromRow() >= 0 && swap.fromRow() < s.routes.size(),
      "fromRow must be >= 0 and < %s, it is %s.", s.routes.size(),
      swap.fromRow());
    checkArgument(swap.toRow() >= 0 && swap.toRow() < s.routes.size(),
      "toRow must be >= 0 and < %s, it is %s.", s.routes.size(), swap.toRow());

    if (swap.fromRow() == swap.toRow()) {
      // 1. swap within same vehicle
      // compute cost of original ordering
      // compute cost of new ordering
      final double originalCost = s.objectiveValues.getDouble(swap.fromRow());
      final ImmutableList<T> newRoute = inListSwap(s.routes.get(swap.fromRow()),
        swap.toIndices(), swap.item());

      final double newCost = computeCost(s, swap.fromRow(), newRoute, cache);
      final double diff = newCost - originalCost;

      if (diff < threshold) {
        // it improves
        final ImmutableList<ImmutableList<T>> newRoutes = replace(s.routes,
          asIntList(swap.fromRow()),
          ImmutableList.of(newRoute));
        final double newObjectiveValue = s.objectiveValue + diff;
        final DoubleList newObjectiveValues = replace(