public void reset(RoutingRequest options) {
   bounder.reset(options);
   Arrays.fill(statesByStop, null);
 }
  /**
   * @param options
   * @param walkOptions
   * @param nBoardings
   * @param createdStates
   * @return whether search should continue
   */
  public boolean walkPhase(
      RoutingRequest options,
      RoutingRequest walkOptions,
      int nBoardings,
      List<RaptorState> createdStates) {

    double distanceToNearestTransitStop = 0;
    if (options.rctx.target != null) {
      distanceToNearestTransitStop = options.rctx.target.getDistanceToNearestTransitStop();
    }

    final int boardSlack =
        nBoardings == 1
            ? options.getBoardSlack()
            : (options.getTransferSlack() - options.getAlightSlack());
    ShortestPathTree spt;
    GenericDijkstra dijkstra = new GenericDijkstra(walkOptions);
    dijkstra.setShortestPathTreeFactory(bounder);
    List<State> transitStopStates = new ArrayList<State>();

    if (nBoardings == 0) {
      // TODO: retry min-time bounding with this and with maxtime

      if (options.rctx.target != null
          && bounder.getTargetDistance(options.rctx.origin) < options.getMaxWalkDistance())
        dijkstra.setHeuristic(bounder);

      MaxWalkState start = new MaxWalkState(options.rctx.origin, walkOptions);
      spt = dijkstra.getShortestPathTree(start);
      for (State state : spt.getAllStates()) {
        if (state.getVertex() instanceof TransitStop
            || state.getVertex() instanceof TransitStopArrive
            || state.getVertex() instanceof TransitStopDepart) transitStopStates.add(state);
      }
      // also, compute an initial spt from the target so that we can find out what transit
      // stops are nearby and what
      // the time is to them, so that we can start target bounding earlier
      if (maxTimeDayIndex > 0) {
        RoutingRequest reversedWalkOptions = walkOptions.clone();
        reversedWalkOptions.setArriveBy(!walkOptions.isArriveBy());
        GenericDijkstra destDijkstra = new GenericDijkstra(reversedWalkOptions);
        start = new MaxWalkState(options.rctx.target, reversedWalkOptions);
        ShortestPathTree targetSpt = destDijkstra.getShortestPathTree(start);
        for (State state : targetSpt.getAllStates()) {

          final Vertex vertex = state.getVertex();

          if (!(vertex instanceof TransitStop)) continue;
          RaptorStop stop = data.raptorStopsForStopId.get(((TransitStop) vertex).getStopId());
          if (stop == null) {
            // we have found a stop is totally unused, so skip it
            continue;
          }

          // Skip banned stops
          if (options.getBannedStops().matches(stop.stopVertex.getStop())) {
            continue;
          }
          if (options.getBannedStopsHard().matches(stop.stopVertex.getStop())) {
            continue;
          }

          addStopNearTarget(stop, state.getWalkDistance(), (int) state.getElapsedTimeSeconds());
        }
      }
    } else {

      final List<MaxWalkState> startPoints = new ArrayList<MaxWalkState>();

      for (RaptorState state : createdStates) {

        // bounding states
        // this reduces the number of initial vertices
        // and the state space size

        Vertex stopVertex =
            options.isArriveBy() ? state.stop.departVertex : state.stop.arriveVertex;
        if (stopVertex == null) {
          stopVertex = state.stop.stopVertex;
        }

        if (options.rctx.target != null) {
          double minWalk = distanceToNearestTransitStop;
          double targetDistance = bounder.getTargetDistance(stopVertex);

          if (targetDistance + state.walkDistance > options.getMaxWalkDistance()) {
            // can't walk to destination, so we can't alight at a local vertex
            if (state.stop.stopVertex.isLocal()) continue;
          }

          if (minWalk + state.walkDistance > options.getMaxWalkDistance()) {
            continue;
          }
        }

        StateEditor dijkstraState = new MaxWalkState.MaxWalkStateEditor(walkOptions, stopVertex);
        dijkstraState.setInitialWaitTimeSeconds(state.initialWaitTime);
        dijkstraState.setStartTimeSeconds(options.dateTime);
        dijkstraState.setNumBoardings(state.nBoardings);
        dijkstraState.setWalkDistance(state.walkDistance);
        dijkstraState.setTimeSeconds(state.arrivalTime);
        dijkstraState.setExtension("raptorParent", state);
        dijkstraState.setOptions(walkOptions);
        dijkstraState.incrementWeight(state.weight);
        MaxWalkState newState = (MaxWalkState) dijkstraState.makeState();
        startPoints.add(newState);
      }
      if (startPoints.size() == 0) {
        return false;
      }
      System.out.println("walk starts: " + startPoints.size() + " / " + visitedEver.size());
      dijkstra.setPriorityQueueFactory(
          new PrefilledPriorityQueueFactory(startPoints.subList(1, startPoints.size())));

      bounder.addSptStates(startPoints.subList(1, startPoints.size()));

      bounder.prepareForSearch();

      dijkstra.setSearchTerminationStrategy(bounder);
      if (options.rctx.target != null) {
        dijkstra.setSkipTraverseResultStrategy(bounder);
        dijkstra.setHeuristic(bounder);
      }

      // Do local search
      spt = dijkstra.getShortestPathTree(startPoints.get(0));
      transitStopStates = bounder.getTransitStopsVisited();
    }

    List<? extends State> targetStates = null;
    if (walkOptions.rctx.target != null) targetStates = spt.getStates(walkOptions.rctx.target);
    if (targetStates != null) {
      TARGET:
      for (State targetState : targetStates) {
        RaptorState parent = (RaptorState) targetState.getExtension("raptorParent");
        RaptorState state;
        if (parent != null) {
          state = new RaptorState(parent);
          state.nBoardings = parent.nBoardings;
          state.rentingBike = targetState.isBikeRenting();
        } else {
          state = new RaptorState(options);
        }
        state.weight = targetState.getWeight();
        state.walkDistance = targetState.getWalkDistance();
        state.arrivalTime = (int) targetState.getTimeSeconds();
        state.walkPath = targetState;
        for (Iterator<RaptorState> it = getTargetStates().iterator(); it.hasNext(); ) {
          RaptorState oldState = it.next();
          if (oldState.eDominates(state)) {
            continue TARGET;
          } else if (state.eDominates(oldState)) {
            it.remove();
          }
        }
        addTargetState(state);
        log.debug("Found target at: " + state + " on " + state.getTrips());
      }
    }
    for (State state : bounder.removedBoundingStates) {
      removeTargetState(state);
    }

    SPTSTATE:
    for (State state : transitStopStates) {
      final Vertex vertex = state.getVertex();

      RaptorStop stop = data.raptorStopsForStopId.get(((OffboardVertex) vertex).getStopId());
      if (stop == null) {
        // we have found a stop is totally unused, so skip it
        continue;
      }

      // Skip banned stops
      if (options.getBannedStops().matches(stop.stopVertex.getStop())) {
        continue;
      }
      if (options.getBannedStopsHard().matches(stop.stopVertex.getStop())) {
        continue;
      }

      if (options.rctx.target != null) {
        double minWalk = distanceToNearestTransitStop;

        double targetDistance = bounder.getTargetDistance(vertex);
        final double remainingWalk = options.maxWalkDistance - state.getWalkDistance();

        if (maxTimeDayIndex > 0 && remainingWalk < 3218) {
          double minTime =
              (targetDistance - minWalk) / Raptor.MAX_TRANSIT_SPEED
                  + minWalk / options.getStreetSpeedUpperBound();
          if (targetDistance > remainingWalk) minTime += boardSlack;

          int maxTimeForVertex = 0;
          int region = vertex.getGroupIndex();
          final int elapsedTime = (int) state.getElapsedTimeSeconds();
          for (StopNearTarget stopNearTarget : stopsNearTarget.values()) {
            int destinationRegion = stopNearTarget.stop.stopVertex.getGroupIndex();
            final int maxTimeFromThisRegion =
                data.maxTransitRegions.maxTransit[maxTimeDayIndex][destinationRegion][region];
            int maxTime = elapsedTime + maxTimeFromThisRegion + stopNearTarget.time;

            if (maxTime > maxTimeForVertex) {
              maxTimeForVertex = maxTime;
            }
          }
          if (maxTimeForVertex < maxTime) {
            maxTime = maxTimeForVertex;
          } else {
            if (elapsedTime + minTime > maxTime * 1.5) {
              continue;
            }
          }
        }
      }
      List<RaptorState> states = statesByStop[stop.index];
      if (states == null) {
        states = new ArrayList<RaptorState>();
        statesByStop[stop.index] = states;
      }

      RaptorState parent = (RaptorState) state.getExtension("raptorParent");
      RaptorState newState;
      if (parent != null) {
        newState = new RaptorState(parent);
      } else {
        // this only happens in round 0
        newState = new RaptorState(options);
      }
      newState.weight = state.getWeight();
      newState.nBoardings = nBoardings;
      newState.walkDistance = state.getWalkDistance();
      newState.arrivalTime = (int) state.getTimeSeconds();
      newState.walkPath = state;
      newState.stop = stop;
      newState.rentingBike = state.isBikeRenting();

      for (RaptorState oldState : states) {
        if (oldState.eDominates(newState)) {
          continue SPTSTATE;
        }
      }

      visitedLastRound.add(stop);
      visitedEver.add(stop);
      states.add(newState);
    }
    return true;
  }