@SuppressWarnings({"unchecked", "rawtypes"})
  @Override
  protected boolean executeMoveImplem(S solution, IMove move) {
    if (!(move instanceof TwoOptMove)) {
      throw new IllegalArgumentException("The move must be of type TwoOptMove");
    }

    boolean r;

    TwoOptMove twoOptMove = (TwoOptMove) move;

    IRoute rI = solution.getRoute(twoOptMove.getRouteI());
    IRoute rJ = null;
    int i = twoOptMove.getI();
    int j = twoOptMove.getJ();

    // Intra route move
    if (twoOptMove.getRouteI() == twoOptMove.getRouteJ()) {
      // Remove (nodeI,nodeI+1) and (nodeJ,nodeJ+1)
      // Reconnect nodeI with nodeJ and nodeI+1 with nodeJ+1
      rI.reverseSubRoute(i + 1, j);

      // rI.updateCost(-twoOptMove.getImprovement());

      r = true;
      // Inter route move
    } else {
      rJ = solution.getRoute(twoOptMove.getRouteJ());

      // Auxiliary method to solve type safety problems
      r = executeMove(rI, rJ, i, j, twoOptMove.isStar());
    }
    return r;
  }
  /**
   * Auxiliary method used for type safety
   *
   * @param <V>
   * @param rI
   * @param rJ
   * @param nodeI
   * @param nodeJ
   * @param star
   * @return
   */
  protected <V extends INodeVisit> boolean executeMove(
      IRoute<V> rI, IRoute<V> rJ, int i, int j, boolean star) {

    if (!star) {
      // standard 2-opt
      // nodeI linked with nodeJ
      // nodeI+1 linked with nodeJ+1

      IRoute<V> startJ = rJ.extractSubroute(0, j);
      IRoute<V> endI = rI.extractSubroute(i + 1, rI.length() - 1);

      startJ.reverseRoute();
      endI.reverseRoute();

      rI.appendRoute(startJ);
      rJ.insertSubroute(0, endI);

    } else {
      // special 2-opt*
      // nodeI linked with nodeJ+1
      // nodeI+1 linked with nodeJ

      IRoute<V> endJ = rJ.extractSubroute(j + 1, rJ.length() - 1);
      IRoute<V> endI = rI.extractSubroute(i + 1, rI.length() - 1);

      rI.appendRoute(endJ);
      rJ.appendRoute(endI);
    }

    return true;
  }