/** Make sure that stops are properly linked into the graph */
  @Test
  public void testStopLinking() throws Exception {
    AddTripPattern atp = getAddTripPattern(RouteSelector.BROAD_HIGH);
    atp.timetables.add(getTimetable(true));

    // get a graph
    Graph g = buildGraphNoTransit();
    link(g);
    g.index(new DefaultStreetVertexIndexFactory());

    // materialize the trip pattern
    atp.materialize(g);

    // there should be five stops because one point is not a stop
    assertEquals(5, atp.temporaryStops.length);

    // they should all be linked into the graph
    for (int i = 0; i < atp.temporaryStops.length; i++) {
      assertNotNull(atp.temporaryStops[i].sample);
      assertNotNull(atp.temporaryStops[i].sample.v0);
      assertNotNull(atp.temporaryStops[i].sample.v1);
    }

    // no services running: not needed for trips added on the fly.
    TimeWindow window = new TimeWindow(7 * 3600, 9 * 3600, new BitSet(), DayOfWeek.WEDNESDAY);

    Scenario scenario = new Scenario(0);
    scenario.modifications = Lists.newArrayList(atp);
    ProfileRequest req = new ProfileRequest();
    req.scenario = scenario;
    req.boardingAssumption = RaptorWorkerTimetable.BoardingAssumption.WORST_CASE;

    RaptorWorkerData data = new RaptorWorkerData(g, window, req);
    assertEquals(5, data.nStops);

    // make sure we can find the stops
    AStar aStar = new AStar();
    RoutingRequest rr = new RoutingRequest(TraverseMode.WALK);
    rr.from = new GenericLocation(39.963417, -82.980799);
    rr.batch = true;
    rr.setRoutingContext(g);
    rr.batch = true;

    ShortestPathTree spt = aStar.getShortestPathTree(rr);

    TIntIntMap stops = data.findStopsNear(spt, g);

    // we should have found stops
    assertFalse(stops.isEmpty());

    // ensure that the times made it into the data
    // This assumes worst-case departure, and the first worst departure is 10:30 after the service
    // starts running (dwell + headway)
    assertEquals(
        4 * 3600 + 600 + 30,
        data.timetablesForPattern.get(0).getFrequencyDeparture(0, 0, 39 * 360, -1, null));
  }
  /** Test the full routing */
  @Test
  public void integrationTest() throws Exception {
    Graph g = buildGraphNoTransit();
    addTransit(g);
    link(g);

    ProfileRequest pr1 = new ProfileRequest();
    pr1.date = new LocalDate(2015, 6, 10);
    pr1.fromTime = 7 * 3600;
    pr1.toTime = 9 * 3600;
    pr1.fromLat = pr1.toLat = 39.9621;
    pr1.fromLon = pr1.toLon = -83.0007;

    RepeatedRaptorProfileRouter rrpr1 = new RepeatedRaptorProfileRouter(g, pr1);
    rrpr1.route();

    ProfileRequest pr2 = new ProfileRequest();
    pr2.date = new LocalDate(2015, 6, 10);
    pr2.fromTime = 7 * 3600;
    pr2.toTime = 9 * 3600;
    pr2.fromLat = pr2.toLat = 39.9621;
    pr2.fromLon = pr2.toLon = -83.0007;

    AddTripPattern atp = getAddTripPattern(RouteSelector.BROAD_HIGH);
    atp.timetables.add(getTimetable(true));

    pr2.scenario = new Scenario(0);
    pr2.scenario.modifications = Arrays.asList(atp);

    RepeatedRaptorProfileRouter rrpr2 = new RepeatedRaptorProfileRouter(g, pr2);
    rrpr2.route();

    boolean foundDecrease = false;

    // make sure that travel time did not increase
    for (TObjectIntIterator<Vertex> vit = rrpr1.timeSurfaceRangeSet.min.times.iterator();
        vit.hasNext(); ) {
      vit.advance();

      int time2 = rrpr2.timeSurfaceRangeSet.min.getTime(vit.key());

      assertTrue(time2 <= vit.value());

      if (time2 < vit.value()) foundDecrease = true;
    }

    assertTrue("found decreases in travel time due to adding route", foundDecrease);
  }
  /** Make sure that transfers work */
  @Test
  public void testTransfers() throws Exception {
    AddTripPattern atp = getAddTripPattern(RouteSelector.BROAD_HIGH);
    atp.timetables.add(getTimetable(false));

    AddTripPattern atp2 = getAddTripPattern(RouteSelector.BEXLEY_CMH);
    atp2.timetables.add(getTimetable(true));

    // get a graph
    Graph g = buildGraphNoTransit();
    addTransit(g);
    link(g);
    g.index(new DefaultStreetVertexIndexFactory());

    // materialize the trip pattern
    atp.materialize(g);
    atp2.materialize(g);

    TimeWindow window =
        new TimeWindow(
            7 * 3600,
            9 * 3600,
            g.index.servicesRunning(new LocalDate(2015, 6, 10)),
            DayOfWeek.WEDNESDAY);

    Scenario scenario = new Scenario(0);
    scenario.modifications = Lists.newArrayList(atp, atp2);
    ProfileRequest req = new ProfileRequest();
    req.scenario = scenario;
    req.boardingAssumption = RaptorWorkerTimetable.BoardingAssumption.WORST_CASE;

    RaptorWorkerData data = new RaptorWorkerData(g, window, req);

    // make sure that we have transfers a) between the new lines b) from the new lines
    // to the existing lines c) from the existing lines to the new lines
    // stop IDs in the data will be 0 and 1 for existing stops, 2 - 6 for Broad/High and 7 - 11 for
    // Bexley/CMH
    int[] txFromExisting = data.transfersForStop.get(0);
    if (txFromExisting.length == 0) txFromExisting = data.transfersForStop.get(1);

    // make sure there's a transfer to stop 4 (Broad/High)
    // the AddTripPattern instructions are processed in order
    // also recall that each transfer has two ints in the array as it's a jagged array of
    // dest_pattern, distance
    assertTrue(txFromExisting.length > 0);

    boolean foundTx = false;

    for (int i = 0; i < txFromExisting.length; i += 2) {
      if (txFromExisting[i] == 4) {
        foundTx = true;
        break;
      }
    }

    assertTrue("transfer from existing to new", foundTx);

    // Check that there are transfers from the new route to the existing route
    // This is the stop at Broad and High
    int[] txToExisting = data.transfersForStop.get(4);
    assertTrue(txToExisting.length > 0);
    foundTx = false;

    for (int i = 0; i < txToExisting.length; i += 2) {
      if (txToExisting[i] == 0 || txToExisting[i] == 1) {
        // stop from existing route
        foundTx = true;
        break;
      }
    }

    assertTrue("transfer from new to existing", foundTx);

    // Check that there are transfers between the new routes
    int[] txBetweenNew = data.transfersForStop.get(7);
    assertTrue(txBetweenNew.length > 0);
    foundTx = false;

    for (int i = 0; i < txBetweenNew.length; i += 2) {
      if (txBetweenNew[i] == 2) {
        foundTx = true;
        break;
      }
    }

    assertTrue(foundTx);
  }