public TargetBound(RoutingRequest options) {
   this.options = options;
   if (options.rctx.target != null) {
     this.realTarget = options.rctx.target;
     this.realTargetCoordinate = realTarget.getCoordinate();
     this.distanceToNearestTransitStop = realTarget.getDistanceToNearestTransitStop();
     bounders = new ArrayList<State>();
     transitLocalStreets = options.rctx.graph.getService(TransitLocalStreetService.class);
     speedUpperBound = options.getSpeedUpperBound();
     this.speedWeight = options.getWalkReluctance() / speedUpperBound;
     this.transferTimeInWalkDistance = options.getTransferSlack() / options.getWalkSpeed();
   }
 }
 public void reset(RoutingRequest options) {
   this.options = options;
   if (realTarget != options.rctx.target) {
     this.realTarget = options.rctx.target;
     this.realTargetCoordinate = realTarget.getCoordinate();
     this.distanceToNearestTransitStop = realTarget.getDistanceToNearestTransitStop();
     bounders = new ArrayList<State>();
     Arrays.fill(distance, -1);
   }
   spt = new ArrayMultiShortestPathTree(options);
   transitLocalStreets = options.rctx.graph.getService(TransitLocalStreetService.class);
   speedUpperBound = options.getSpeedUpperBound();
   this.speedWeight = options.getWalkReluctance() / speedUpperBound;
 }
  @Override
  public List<GraphPath> plan(State origin, Vertex target, int nItineraries) {

    TraverseOptions options = origin.getOptions();

    if (_graphService.getCalendarService() != null)
      options.setCalendarService(_graphService.getCalendarService());
    options.setTransferTable(_graphService.getGraph().getTransferTable());

    options.setServiceDays(origin.getTime(), _graphService.getGraph().getAgencyIds());
    if (options.getModes().getTransit()
        && !_graphService.getGraph().transitFeedCovers(new Date(origin.getTime() * 1000))) {
      // user wants a path through the transit network,
      // but the date provided is outside those covered by the transit feed.
      throw new TransitTimesException();
    }

    // always use the bidirectional heuristic because the others are not precise enough
    RemainingWeightHeuristic heuristic =
        new BidirectionalRemainingWeightHeuristic(_graphService.getGraph());

    // the states that will eventually be turned into paths and returned
    List<State> returnStates = new LinkedList<State>();

    // Populate any extra edges
    final ExtraEdgesStrategy extraEdgesStrategy = options.extraEdgesStrategy;
    OverlayGraph extraEdges = new OverlayGraph();
    extraEdgesStrategy.addEdgesFor(extraEdges, origin.getVertex());
    extraEdgesStrategy.addEdgesFor(extraEdges, target);

    BinHeap<State> pq = new BinHeap<State>();
    //        List<State> boundingStates = new ArrayList<State>();

    // initialize heuristic outside loop so table can be reused
    heuristic.computeInitialWeight(origin, target);

    // increase maxWalk repeatedly in case hard limiting is in use
    WALK:
    for (double maxWalk = options.getMaxWalkDistance();
        maxWalk < 100000 && returnStates.isEmpty();
        maxWalk *= 2) {
      LOG.debug("try search with max walk {}", maxWalk);
      // increase maxWalk if settings make trip impossible
      if (maxWalk
          < Math.min(
              origin.getVertex().distance(target),
              origin.getVertex().getDistanceToNearestTransitStop()
                  + target.getDistanceToNearestTransitStop())) continue WALK;
      options.setMaxWalkDistance(maxWalk);
      // reinitialize states for each retry
      HashMap<Vertex, List<State>> states = new HashMap<Vertex, List<State>>();
      pq.reset();
      pq.insert(origin, 0);
      long startTime = System.currentTimeMillis();
      long endTime = startTime + (int) (_timeouts[0] * 1000);
      LOG.debug("starttime {} endtime {}", startTime, endTime);
      QUEUE:
      while (!pq.empty()) {

        if (System.currentTimeMillis() > endTime) {
          LOG.debug("timeout at {} msec", System.currentTimeMillis() - startTime);
          if (returnStates.isEmpty()) continue WALK;
          else {
            storeMemory();
            break WALK;
          }
        }

        State su = pq.extract_min();

        //                for (State bs : boundingStates) {
        //                    if (eDominates(bs, su)) {
        //                        continue QUEUE;
        //                    }
        //                }

        Vertex u = su.getVertex();

        if (traverseVisitor != null) {
          traverseVisitor.visitVertex(su);
        }

        if (u.equals(target)) {
          //                    boundingStates.add(su);
          returnStates.add(su);
          if (!options.getModes().getTransit()) break QUEUE;
          // options should contain max itineraries
          if (returnStates.size() >= _maxPaths) break QUEUE;
          if (returnStates.size() < _timeouts.length) {
            endTime = startTime + (int) (_timeouts[returnStates.size()] * 1000);
            LOG.debug(
                "{} path, set timeout to {}",
                returnStates.size(),
                _timeouts[returnStates.size()] * 1000);
          }
          continue QUEUE;
        }

        for (Edge e : u.getEdges(extraEdges, null, options.isArriveBy())) {
          STATE:
          for (State new_sv = e.traverse(su); new_sv != null; new_sv = new_sv.getNextResult()) {
            if (traverseVisitor != null) {
              traverseVisitor.visitEdge(e, new_sv);
            }

            double h = heuristic.computeForwardWeight(new_sv, target);
            //                    for (State bs : boundingStates) {
            //                        if (eDominates(bs, new_sv)) {
            //                            continue STATE;
            //                        }
            //                    }
            Vertex v = new_sv.getVertex();
            List<State> old_states = states.get(v);
            if (old_states == null) {
              old_states = new LinkedList<State>();
              states.put(v, old_states);
            } else {
              for (State old_sv : old_states) {
                if (eDominates(old_sv, new_sv)) {
                  continue STATE;
                }
              }
              Iterator<State> iter = old_states.iterator();
              while (iter.hasNext()) {
                State old_sv = iter.next();
                if (eDominates(new_sv, old_sv)) {
                  iter.remove();
                }
              }
            }
            if (traverseVisitor != null) traverseVisitor.visitEnqueue(new_sv);

            old_states.add(new_sv);
            pq.insert(new_sv, new_sv.getWeight() + h);
          }
        }
      }
    }
    storeMemory();

    // Make the states into paths and return them
    List<GraphPath> paths = new LinkedList<GraphPath>();
    for (State s : returnStates) {
      LOG.debug(s.toStringVerbose());
      paths.add(new GraphPath(s, true));
    }
    // sort by arrival time, though paths are already in order of increasing difficulty
    // Collections.sort(paths, new PathComparator(origin.getOptions().isArriveBy()));
    return paths;
  }
  @Override
  public boolean shouldSkipTraversalResult(
      Vertex origin,
      Vertex target,
      State parent,
      State current,
      ShortestPathTree spt,
      RoutingRequest traverseOptions) {
    if (realTarget == null) return false;

    final Vertex vertex = current.getVertex();
    int vertexIndex = vertex.getIndex();
    if (vertexIndex < distance.length) {
      if (distance[vertexIndex] > 0.0) {
        targetDistance = distance[vertexIndex];
      } else {
        targetDistance =
            distanceLibrary.fastDistance(
                realTargetCoordinate.y, realTargetCoordinate.x, vertex.getY(), vertex.getX());
        distance[vertexIndex] = targetDistance;
        if (vertex instanceof TransitStop && targetDistance < bestTargetDistance) {
          bestTargetDistance = targetDistance;
        }
      }
    } else {
      targetDistance =
          distanceLibrary.fastDistance(
              realTargetCoordinate.y, realTargetCoordinate.x, vertex.getY(), vertex.getX());
    }

    final double remainingWalk = traverseOptions.maxWalkDistance - current.getWalkDistance();
    final double minWalk;
    double minTime = 0;
    if (targetDistance > remainingWalk) {
      // then we must have some transit + some walk.
      minWalk = this.distanceToNearestTransitStop + vertex.getDistanceToNearestTransitStop();
      minTime =
          options.isArriveBy() ? traverseOptions.getAlightSlack() : traverseOptions.getBoardSlack();

      if (current.getBackEdge() instanceof StreetEdge
          && transitLocalStreets != null
          && !transitLocalStreets.transferrable(vertex)) {
        return true;
      }
    } else {
      // could walk directly to destination
      if (targetDistance < distanceToNearestTransitStop
          || transitLocalStreets == null
          || !transitLocalStreets.transferrable(vertex)) minWalk = targetDistance;
      else minWalk = distanceToNearestTransitStop;
    }
    if (minWalk > remainingWalk) return true;

    final double optimisticDistance = current.getWalkDistance() + minWalk;

    final double walkTime = minWalk / speedUpperBound;
    minTime += (targetDistance - minWalk) / Raptor.MAX_TRANSIT_SPEED + walkTime;

    double stateTime = current.getOptimizedElapsedTime() + minTime;

    double walkDistance =
        FastMath.max(
            optimisticDistance * Raptor.WALK_EPSILON,
            optimisticDistance + transferTimeInWalkDistance);

    int i = 0;
    boolean prevBounded = !bounders.isEmpty();
    for (State bounder : bounders) {
      if (removedBoundingStates.contains(bounder)) continue;
      if (current.getWeight() + minTime + walkTime * (options.getWalkReluctance() - 1)
          > bounder.getWeight() * WORST_WEIGHT_DIFFERENCE_FACTOR) {
        return true;
      }
      int prevTime = previousArrivalTime.get(i++);

      if (walkDistance > bounder.getWalkDistance()
          && current.getNumBoardings() >= bounder.getNumBoardings()) {
        if (current.getElapsedTime() + minTime >= bounder.getElapsedTime()) {
          return true;
        } else if (prevTime > 0
            && (options.arriveBy
                ? (current.getTime() - minTime >= prevTime)
                : ((current.getTime() + minTime) <= prevTime))) {
          prevBounded = false;
        }
      } else {
        prevBounded = false;
      }

      // check that the new path is not much longer in time than the bounding path
      if (bounder.getOptimizedElapsedTime() * timeBoundFactor < stateTime) {
        return true;
      }
    }
    return prevBounded;
  }
  @Override
  public List<GraphPath> getPaths(RoutingRequest options) {

    if (options.rctx == null) {
      options.setRoutingContext(graphService.getGraph(options.getRouterId()));
      // move into setRoutingContext ?
      options.rctx.pathParsers =
          new PathParser[] {new BasicPathParser(), new NoThruTrafficPathParser()};
    }

    RemainingWeightHeuristic heuristic;
    if (options.getModes().isTransit()) {
      LOG.debug("Transit itinerary requested.");
      // always use the bidirectional heuristic because the others are not precise enough
      heuristic = new BidirectionalRemainingWeightHeuristic(options.rctx.graph);
    } else {
      LOG.debug("Non-transit itinerary requested.");
      heuristic = new DefaultRemainingWeightHeuristic();
    }

    // the states that will eventually be turned into paths and returned
    List<State> returnStates = new LinkedList<State>();

    BinHeap<State> pq = new BinHeap<State>();
    //        List<State> boundingStates = new ArrayList<State>();

    Vertex originVertex = options.rctx.origin;
    Vertex targetVertex = options.rctx.target;

    // increase maxWalk repeatedly in case hard limiting is in use
    WALK:
    for (double maxWalk = options.getMaxWalkDistance(); returnStates.isEmpty(); maxWalk *= 2) {
      if (maxWalk != Double.MAX_VALUE && maxWalk > MAX_WALK) {
        break;
      }
      LOG.debug("try search with max walk {}", maxWalk);
      // increase maxWalk if settings make trip impossible
      if (maxWalk
          < Math.min(
              distanceLibrary.distance(originVertex.getCoordinate(), targetVertex.getCoordinate()),
              originVertex.getDistanceToNearestTransitStop()
                  + targetVertex.getDistanceToNearestTransitStop())) continue WALK;
      options.setMaxWalkDistance(maxWalk);

      // cap search / heuristic weight
      final double AVG_TRANSIT_SPEED = 25; // m/sec
      double cutoff =
          (distanceLibrary.distance(originVertex.getCoordinate(), targetVertex.getCoordinate())
                  * 1.5)
              / AVG_TRANSIT_SPEED; // wait time is irrelevant in the heuristic
      cutoff += options.getMaxWalkDistance() * options.walkReluctance;
      options.maxWeight = cutoff;

      State origin = new State(options);
      // (used to) initialize heuristic outside loop so table can be reused
      heuristic.computeInitialWeight(origin, targetVertex);

      options.maxWeight = cutoff + 30 * 60 * options.waitReluctance;

      // reinitialize states for each retry
      HashMap<Vertex, List<State>> states = new HashMap<Vertex, List<State>>();
      pq.reset();
      pq.insert(origin, 0);
      long startTime = System.currentTimeMillis();
      long endTime = startTime + (int) (_timeouts[0] * 1000);
      LOG.debug("starttime {} endtime {}", startTime, endTime);
      QUEUE:
      while (!pq.empty()) {

        if (System.currentTimeMillis() > endTime) {
          LOG.debug("timeout at {} msec", System.currentTimeMillis() - startTime);
          if (returnStates.isEmpty()) break WALK; // disable walk distance increases
          else {
            storeMemory();
            break WALK;
          }
        }

        //                if (pq.peek_min_key() > options.maxWeight) {
        //                    LOG.debug("max weight {} exceeded", options.maxWeight);
        //                    break QUEUE;
        //                }

        State su = pq.extract_min();

        //                for (State bs : boundingStates) {
        //                    if (eDominates(bs, su)) {
        //                        continue QUEUE;
        //                    }
        //                }

        Vertex u = su.getVertex();

        if (traverseVisitor != null) {
          traverseVisitor.visitVertex(su);
        }

        if (u.equals(targetVertex)) {
          //                    boundingStates.add(su);
          returnStates.add(su);
          if (!options.getModes().isTransit()) break QUEUE;
          // options should contain max itineraries
          if (returnStates.size() >= _maxPaths) break QUEUE;
          if (returnStates.size() < _timeouts.length) {
            endTime = startTime + (int) (_timeouts[returnStates.size()] * 1000);
            LOG.debug(
                "{} path, set timeout to {}",
                returnStates.size(),
                _timeouts[returnStates.size()] * 1000);
          }
          continue QUEUE;
        }

        for (Edge e : options.isArriveBy() ? u.getIncoming() : u.getOutgoing()) {
          STATE:
          for (State new_sv = e.traverse(su); new_sv != null; new_sv = new_sv.getNextResult()) {
            if (traverseVisitor != null) {
              traverseVisitor.visitEdge(e, new_sv);
            }

            double h = heuristic.computeForwardWeight(new_sv, targetVertex);
            if (h == Double.MAX_VALUE) continue;
            //                    for (State bs : boundingStates) {
            //                        if (eDominates(bs, new_sv)) {
            //                            continue STATE;
            //                        }
            //                    }
            Vertex v = new_sv.getVertex();
            List<State> old_states = states.get(v);
            if (old_states == null) {
              old_states = new LinkedList<State>();
              states.put(v, old_states);
            } else {
              for (State old_sv : old_states) {
                if (eDominates(old_sv, new_sv)) {
                  continue STATE;
                }
              }
              Iterator<State> iter = old_states.iterator();
              while (iter.hasNext()) {
                State old_sv = iter.next();
                if (eDominates(new_sv, old_sv)) {
                  iter.remove();
                }
              }
            }
            if (traverseVisitor != null) traverseVisitor.visitEnqueue(new_sv);

            old_states.add(new_sv);
            pq.insert(new_sv, new_sv.getWeight() + h);
          }
        }
      }
    }
    storeMemory();

    // Make the states into paths and return them
    List<GraphPath> paths = new LinkedList<GraphPath>();
    for (State s : returnStates) {
      LOG.debug(s.toStringVerbose());
      paths.add(new GraphPath(s, true));
    }
    // sort by arrival time, though paths are already in order of increasing difficulty
    // Collections.sort(paths, new PathComparator(origin.getOptions().isArriveBy()));
    return paths;
  }