public void testFewestTransfers() {
    Vertex stop_c = graph.getVertex("agency:C");
    Vertex stop_d = graph.getVertex("agency:D");
    RoutingRequest options = new RoutingRequest();
    options.optimize = OptimizeType.QUICK;
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 0, 0);
    options.setRoutingContext(graph, stop_c, stop_d);

    ShortestPathTree spt = aStar.getShortestPathTree(options);

    // when optimizing for speed, take the fast two-bus path
    GraphPath path = spt.getPath(stop_d, false);
    assertNotNull(path);
    assertEquals(
        TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 20, 0), path.getEndTime());

    // when optimizing for fewest transfers, take the slow one-bus path
    options.transferPenalty = 1800;
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_d, false);
    assertNotNull(path);
    assertEquals(
        TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 50, 0), path.getEndTime());
  }
  public void testRoutingOverMidnight() throws Exception {
    // this route only runs on weekdays
    Vertex stop_g = graph.getVertex("agency:G_depart");
    Vertex stop_h = graph.getVertex("agency:H_arrive");

    ShortestPathTree spt;
    GraphPath path;
    RoutingRequest options = new RoutingRequest();

    // Friday evening
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 23, 20, 0);
    options.setRoutingContext(graph, stop_g, stop_h);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_h, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());

    // Saturday morning
    long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 0, 5, 0);
    options.dateTime = startTime;
    options.setRoutingContext(graph, stop_g.getLabel(), stop_h.getLabel());
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_h, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());
    long endTime = path.getEndTime();
    assertTrue(endTime < startTime + 60 * 60);
  }
  public void testPickupDropoff() throws Exception {
    Vertex stop_o = graph.getVertex("agency_O_depart");
    Vertex stop_p = graph.getVertex("agency_P");
    int i = 0;
    for (@SuppressWarnings("unused") Edge e : stop_o.getOutgoing()) {
      ++i;
    }
    assertTrue(i == 3);

    long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 12, 0, 0);
    RoutingRequest options = new RoutingRequest();
    options.dateTime = startTime;
    options.setRoutingContext(graph, stop_o, stop_p);
    ShortestPathTree spt = aStar.getShortestPathTree(options);
    GraphPath path = spt.getPath(stop_p, false);
    assertNotNull(path);
    long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 12, 10, 0);
    assertEquals(endTime, path.getEndTime());

    startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 12, 0, 1);
    options.dateTime = startTime;
    options.setRoutingContext(graph, stop_o, stop_p);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_p, false);
    assertNotNull(path);
    endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 15, 10, 0);
    assertEquals(endTime, path.getEndTime());
  }
  public void testWheelchairAccessible() throws Exception {
    Vertex near_a = graph.getVertex("near_1_agency_entrance_a");
    Vertex near_b = graph.getVertex("near_1_agency_entrance_b");
    Vertex near_c = graph.getVertex("near_1_agency_C");
    Vertex near_e = graph.getVertex("near_1_agency_E");

    Vertex stop_d = graph.getVertex("agency:D");
    Vertex split_d = null;
    for (StreetTransitLink e : Iterables.filter(stop_d.getOutgoing(), StreetTransitLink.class)) {
      split_d = e.getToVertex();
    }

    RoutingRequest options = new RoutingRequest();
    options.wheelchairAccessible = true;
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 0, 0, 0);

    ShortestPathTree spt;
    GraphPath path;

    // stop B is accessible, so there should be a path.
    options.setRoutingContext(graph, near_a, near_b);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(near_b, false);
    assertNotNull(path);

    // stop C is not accessible, so there should be no path.
    options.setRoutingContext(graph, near_a, near_c);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(near_c, false);
    assertNull(path);

    // stop E has no accessibility information, but we should still be able to route to it.
    options.setRoutingContext(graph, near_a, near_e);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(near_e, false);
    assertNotNull(path);

    // from stop A to stop D would normally be trip 1.1 to trip 2.1, arriving at 00:30. But trip
    // 2 is not accessible, so we'll do 1.1 to 3.1, arriving at 01:00
    GregorianCalendar time = new GregorianCalendar(2009, 8, 18, 0, 0, 0);
    time.setTimeZone(TimeZone.getTimeZone("America/New_York"));
    options.dateTime = TestUtils.toSeconds(time);
    options.setRoutingContext(graph, near_a, split_d);
    spt = aStar.getShortestPathTree(options);

    time.add(Calendar.HOUR, 1);
    time.add(Calendar.SECOND, 1); // for the StreetTransitLink
    path = spt.getPath(split_d, false);
    assertNotNull(path);
    assertEquals(TestUtils.toSeconds(time), path.getEndTime());
  }
  public void testTimelessStops() throws Exception {
    Vertex stop_d = graph.getVertex("agency:D");
    Vertex stop_c = graph.getVertex("agency:C");
    RoutingRequest options = new RoutingRequest();
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 10, 0, 0);
    options.setRoutingContext(graph, stop_d, stop_c);
    ShortestPathTree spt = aStar.getShortestPathTree(options);

    GraphPath path = spt.getPath(stop_c, false);
    assertNotNull(path);
    assertEquals(
        TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 11, 0, 0), path.getEndTime());
  }
  public void testRouting() throws Exception {

    Vertex stop_a = graph.getVertex("agency_A");
    Vertex stop_b = graph.getVertex("agency_B");
    Vertex stop_c = graph.getVertex("agency_C");
    Vertex stop_d = graph.getVertex("agency_D");
    Vertex stop_e = graph.getVertex("agency_E");

    RoutingRequest options = new RoutingRequest();
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0);

    ShortestPathTree spt;
    GraphPath path;

    // A to B
    options.setRoutingContext(graph, stop_a, stop_b);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_b, false);
    assertNotNull(path);
    assertEquals(extractStopVertices(path), Lists.newArrayList(stop_a, stop_b));

    // A to C
    options.setRoutingContext(graph, stop_a, stop_c);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_c, false);
    assertNotNull(path);
    assertEquals(extractStopVertices(path), Lists.newArrayList(stop_a, stop_c));

    // A to D
    options.setRoutingContext(graph, stop_a, stop_d);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_d, false);
    assertNotNull(path);
    assertEquals(extractStopVertices(path), Lists.newArrayList(stop_a, stop_c, stop_d));
    long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0) + 40 * 60;
    assertEquals(endTime, path.getEndTime());

    // A to E
    options.setRoutingContext(graph, stop_a, stop_e);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_e, false);
    assertNotNull(path);
    assertEquals(extractStopVertices(path), Lists.newArrayList(stop_a, stop_c, stop_e));
    endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0) + 70 * 60;
    assertEquals(endTime, path.getEndTime());
  }
  public void testDwell() throws Exception {
    Vertex stop_a = graph.getVertex("agency_A_depart");
    Vertex stop_c = graph.getVertex("agency_C_arrive");

    RoutingRequest options = new RoutingRequest();
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 8, 0, 0);
    options.setRoutingContext(graph, stop_a, stop_c);
    ShortestPathTree spt = aStar.getShortestPathTree(options);

    GraphPath path = spt.getPath(stop_c, false);
    assertNotNull(path);
    assertEquals(6, path.states.size());
    long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 8, 30, 0);
    assertEquals(endTime, path.getEndTime());
  }
  public void testTransfers() throws Exception {
    TransferTable transferTable = graph.getTransferTable();

    // create dummy routes and trips
    Route fromRoute = new Route();
    fromRoute.setId(new AgencyAndId("agency", "1"));
    Trip fromTrip = new Trip();
    fromTrip.setId(new AgencyAndId("agency", "1.1"));
    fromTrip.setRoute(fromRoute);
    Route toRoute = new Route();
    toRoute.setId(new AgencyAndId("agency", "2"));
    Trip toTrip = new Trip();
    toTrip.setId(new AgencyAndId("agency", "2.1"));
    toTrip.setRoute(toRoute);
    Trip toTrip2 = new Trip();
    toTrip2.setId(new AgencyAndId("agency", "2.2"));
    toTrip2.setRoute(toRoute);

    // find stops
    Stop stopK = ((TransitStopArrive) graph.getVertex("agency_K_arrive")).getStop();
    Stop stopN = ((TransitStopDepart) graph.getVertex("agency_N_depart")).getStop();
    Stop stopM = ((TransitStopDepart) graph.getVertex("agency_M_depart")).getStop();

    assertTrue(transferTable.hasPreferredTransfers());
    assertEquals(
        StopTransfer.UNKNOWN_TRANSFER,
        transferTable.getTransferTime(stopN, stopM, fromTrip, toTrip));
    assertEquals(
        StopTransfer.FORBIDDEN_TRANSFER,
        transferTable.getTransferTime(stopK, stopM, fromTrip, toTrip));
    assertEquals(
        StopTransfer.PREFERRED_TRANSFER,
        transferTable.getTransferTime(stopN, stopK, toTrip, toTrip2));
    assertEquals(
        StopTransfer.TIMED_TRANSFER, transferTable.getTransferTime(stopN, stopK, fromTrip, toTrip));
    assertEquals(15, transferTable.getTransferTime(stopN, stopK, fromTrip, toTrip2));

    Vertex e_arrive = graph.getVertex("agency_E_arrive");
    Vertex f_depart = graph.getVertex("agency_F_depart");
    Edge edge = new TransferEdge(e_arrive, f_depart, 10000, 10000);

    long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 0, 50, 0);
    Vertex stop_b = graph.getVertex("agency_B_depart");
    Vertex stop_g = graph.getVertex("agency_G_arrive");
    RoutingRequest options = new RoutingRequest();
    options.dateTime = startTime;
    options.setRoutingContext(graph, stop_b, stop_g);
    ShortestPathTree spt = aStar.getShortestPathTree(options);

    GraphPath path = spt.getPath(stop_g, false);
    assertNotNull(path);

    assertTrue(
        "expected to use much later trip due to min transfer time",
        path.getEndTime() - startTime > 4.5 * 60 * 60);

    /* cleanup */
    e_arrive.removeOutgoing(edge);
    f_depart.removeIncoming(edge);
  }
  public void testRouting() throws Exception {

    Vertex stop_a = graph.getVertex("agency:A");
    Vertex stop_b = graph.getVertex("agency:B");
    Vertex stop_c = graph.getVertex("agency:C");
    Vertex stop_d = graph.getVertex("agency:D");
    Vertex stop_e = graph.getVertex("agency:E");

    RoutingRequest options = new RoutingRequest();
    // test feed is designed for instantaneous transfers
    options.transferSlack = (0);

    long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0);
    options.dateTime = startTime;

    ShortestPathTree spt;
    GraphPath path;

    // A to B
    options.setRoutingContext(graph, stop_a, stop_b);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_b, false);
    assertNotNull(path);
    assertEquals(6, path.states.size());

    // A to C
    options.setRoutingContext(graph, stop_a, stop_c);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_c, false);
    assertNotNull(path);
    assertEquals(8, path.states.size());

    // A to D (change at C)
    options.setRoutingContext(graph, stop_a, stop_d);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_d, false);
    assertNotNull(path);
    // there are two paths of different lengths
    // both arrive at 40 minutes after midnight
    List<TransitStop> stops = extractStopVertices(path);
    assertEquals(stops.size(), 3);
    assertEquals(stops.get(1), stop_c);
    long endTime = startTime + 40 * 60;
    assertEquals(endTime, path.getEndTime());

    // A to E (change at C)
    options.setRoutingContext(graph, stop_a, stop_e);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_e, false);
    assertNotNull(path);
    stops = extractStopVertices(path);
    assertEquals(stops.size(), 3);
    assertEquals(stops.get(1), stop_c);
    endTime = startTime + 70 * 60;
    assertEquals(endTime, path.getEndTime());
  }
  public void testPathways() throws Exception {

    Vertex entrance = graph.getVertex("agency:entrance_a");
    assertNotNull(entrance);
    Vertex stop = graph.getVertex("agency:A");
    assertNotNull(stop);

    RoutingRequest options = new RoutingRequest();
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 0, 0);
    options.setRoutingContext(graph, entrance, stop);
    ShortestPathTree spt = aStar.getShortestPathTree(options);

    GraphPath path = spt.getPath(stop, false);
    assertNotNull(path);
    assertEquals(
        TestUtils.dateInSeconds("America/New_York", 2009, 8, 1, 16, 0, 34), path.getEndTime());
  }
  /** Test that useless dwell edges are in fact removed. */
  public void testDwellSimplification() {
    Vertex stop_f = graph.getVertex("agency_F_depart");
    Vertex stop_h = graph.getVertex("agency_H_arrive");

    RoutingRequest options = new RoutingRequest();
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 5, 0, 0);
    options.setRoutingContext(graph, stop_f, stop_h);

    ShortestPathTree spt = aStar.getShortestPathTree(options);
    GraphPath path = spt.getPath(stop_h, false);
    assertNotNull(path);
    assertEquals(5, path.states.size());
  }
  public void testFrequencies() {
    Vertex stop_u = graph.getVertex("agency_U_depart");
    Vertex stop_v = graph.getVertex("agency_V_arrive");

    ShortestPathTree spt;
    GraphPath path;

    RoutingRequest options = new RoutingRequest();
    options.setModes(new TraverseModeSet("TRANSIT"));
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0);
    options.setRoutingContext(graph, stop_u, stop_v);

    // U to V - original stop times - shouldn't be used
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_v, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());
    long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 6, 40, 0);
    assertEquals(endTime, path.getEndTime());

    // U to V - first frequency
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 7, 0, 0);
    options.setRoutingContext(graph, stop_u, stop_v);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_v, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());
    endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 7, 40, 0);
    assertEquals(endTime, path.getEndTime());

    // U to V - second frequency
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 14, 0, 0);
    options.setRoutingContext(graph, stop_u, stop_v);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_v, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());
    endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 14, 40, 0);
    assertEquals(endTime, path.getEndTime());

    boolean boarded = false;
    for (FrequencyBoard e : filter(stop_u.getOutgoing(), FrequencyBoard.class)) {
      boarded = true;
      FrequencyBoard board = (FrequencyBoard) e;
      FrequencyBasedTripPattern pattern = board.getPattern();
      int previousArrivalTime = pattern.getPreviousArrivalTime(0, 0, false, false, false);
      assertTrue(previousArrivalTime < 0);

      previousArrivalTime = pattern.getPreviousArrivalTime(0, 60 * 60 * 7 - 1, false, false, false);
      assertEquals(60 * 60 * 6, previousArrivalTime);

      previousArrivalTime = pattern.getPreviousArrivalTime(0, 60 * 60 * 11, false, false, false);
      assertEquals(60 * 60 * 10, previousArrivalTime);

      previousArrivalTime = pattern.getPreviousArrivalTime(0, 60 * 60 * 18, false, false, false);
      assertEquals(60 * 60 * 16, previousArrivalTime);
    }
    assertTrue(boarded);
  }
  public void testRunForTrain() {
    /**
     * This is the notorious Byrd bug: we're going from Q to T at 8:30. There's a trip from S to T
     * at 8:50 and a second one at 9:50. To get to S by 8:50, we need to take trip 12.1 from Q to R,
     * and 13.1 from R to S. If we take the direct-but-slower 11.1, we'll miss the 8:50 and have to
     * catch the 9:50.
     */
    Vertex destination = graph.getVertex("agency:T");
    RoutingRequest options = new RoutingRequest();
    // test is designed such that transfers must be instantaneous
    options.transferSlack = (0);
    GregorianCalendar startTime = new GregorianCalendar(2009, 11, 2, 8, 30, 0);
    startTime.setTimeZone(TimeZone.getTimeZone("America/New_York"));
    options.dateTime = TestUtils.toSeconds(startTime);
    options.setRoutingContext(graph, "agency:Q", destination.getLabel());
    ShortestPathTree spt = aStar.getShortestPathTree(options);
    GraphPath path = spt.getPath(destination, false);

    long endTime = path.getEndTime();
    Calendar c = new GregorianCalendar();
    c.setTimeInMillis(endTime * 1000L);
    assertTrue(endTime - TestUtils.toSeconds(startTime) < 7200);
  }
  public void testTraverseMode() throws Exception {
    Vertex stop_a = graph.getVertex("agency:A_depart");
    Vertex stop_b = graph.getVertex("agency:B_arrive");

    ShortestPathTree spt;

    RoutingRequest options = new RoutingRequest();
    options.setModes(new TraverseModeSet("TRAINISH"));
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 0, 0, 0, 0);
    options.setRoutingContext(graph, stop_a, stop_b);
    spt = aStar.getShortestPathTree(options);

    // a to b is bus only
    assertNull(spt.getPath(stop_b, false));

    options.setModes(new TraverseModeSet("TRAINISH,BUSISH"));
    spt = aStar.getShortestPathTree(options);

    assertNotNull(spt.getPath(stop_b, false));
  }
  public void testInterlining() throws Exception {
    Vertex stop_i = graph.getVertex("agency_I_depart");
    Vertex stop_k = graph.getVertex("agency_K_arrive");

    long startTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 19, 12, 0, 0);
    RoutingRequest options = new RoutingRequest();
    options.dateTime = startTime;
    options.setRoutingContext(graph, stop_i, stop_k);
    ShortestPathTree spt = aStar.getShortestPathTree(options);

    GraphPath path = spt.getPath(stop_k, false);
    int num_alights = 0;
    for (State s : path.states) {
      if (s.getBackEdge() instanceof TransitBoardAlight
          && !((TransitBoardAlight) s.getBackEdge()).isBoarding()) {
        num_alights += 1;
      }
      if (s.getBackEdge() instanceof PatternDwell) {
        assertEquals(10 * 60, s.getTimeDeltaSeconds());
      }
    }
    assertEquals(1, num_alights);

    options.setArriveBy(true);
    options.setRoutingContext(graph, stop_i, stop_k);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_i, false);
    //        path.reverse();
    num_alights = 0;
    for (State s : path.states) {
      if (s.getBackEdge() instanceof TransitBoardAlight
          && !((TransitBoardAlight) s.getBackEdge()).isBoarding()) {
        num_alights += 1;
      }
      if (s.getBackEdge() instanceof PatternDwell) {
        assertEquals(10 * 60, s.getTimeDeltaSeconds());
      }
    }
    assertEquals(1, num_alights);
  }
  public void testTripBikesAllowed() throws Exception {
    Vertex stop_a = graph.getVertex("agency:A");
    Vertex stop_b = graph.getVertex("agency:B");
    Vertex stop_c = graph.getVertex("agency:C");
    Vertex stop_d = graph.getVertex("agency:D");

    RoutingRequest options = new RoutingRequest();
    options.modes.setWalk(false);
    options.modes.setBicycle(true);
    options.modes.setTransit(true);
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 18, 0, 0, 0);
    options.setRoutingContext(graph, stop_a, stop_b);

    ShortestPathTree spt;
    GraphPath path;

    // route: bikes allowed, trip: no value
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_b, false);
    assertNotNull(path);

    // route: bikes allowed, trip: bikes not allowed
    options.setRoutingContext(graph, stop_d, stop_c);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_c, false);
    assertNull(path);

    // route: bikes not allowed, trip: bikes allowed
    options.setRoutingContext(graph, stop_c, stop_d);
    spt = aStar.getShortestPathTree(options);

    path = spt.getPath(stop_d, false);
    assertNotNull(path);
  }
  public void testFrequencies() {
    Vertex stop_u = graph.getVertex("agency:U_depart");
    Vertex stop_v = graph.getVertex("agency:V_arrive");

    ShortestPathTree spt;
    GraphPath path;

    RoutingRequest options = new RoutingRequest();
    options.setModes(new TraverseModeSet("TRANSIT"));
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 0, 0, 0);
    options.setRoutingContext(graph, stop_u, stop_v);

    // U to V - original stop times - shouldn't be used
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_v, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());
    long endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 6, 40, 0);
    assertEquals(endTime, path.getEndTime());

    // U to V - first frequency
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 7, 0, 0);
    options.setRoutingContext(graph, stop_u, stop_v);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_v, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());
    endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 7, 40, 0);
    assertEquals(endTime, path.getEndTime());

    // U to V - second frequency
    options.dateTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 14, 0, 0);
    options.setRoutingContext(graph, stop_u, stop_v);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_v, false);
    assertNotNull(path);
    assertEquals(4, path.states.size());
    endTime = TestUtils.dateInSeconds("America/New_York", 2009, 8, 7, 14, 40, 0);
    assertEquals(endTime, path.getEndTime());

    // TODO more detailed testing of frequencies

  }
  public void testHalfEdges() {
    // the shortest half-edge from the start vertex takes you down, but the shortest total path
    // is up and over

    TraverseOptions options = new TraverseOptions();

    HashSet<Edge> turns = new HashSet<Edge>(graph.getOutgoing(left));
    turns.addAll(graph.getOutgoing(leftBack));

    StreetLocation start =
        StreetLocation.createStreetLocation(
            "start",
            "start",
            cast(turns, StreetEdge.class),
            new LinearLocation(0, 0.4).getCoordinate(left.getGeometry()));

    HashSet<Edge> endTurns = new HashSet<Edge>(graph.getOutgoing(right));
    endTurns.addAll(graph.getOutgoing(rightBack));

    StreetLocation end =
        StreetLocation.createStreetLocation(
            "end",
            "end",
            cast(endTurns, StreetEdge.class),
            new LinearLocation(0, 0.8).getCoordinate(right.getGeometry()));

    assertTrue(start.getX() < end.getX());
    assertTrue(start.getY() < end.getY());

    List<DirectEdge> extra = end.getExtra();

    assertEquals(12, extra.size());

    GregorianCalendar startTime = new GregorianCalendar(2009, 11, 1, 12, 34, 25);

    ShortestPathTree spt1 =
        AStar.getShortestPathTree(graph, brOut, end, TestUtils.toSeconds(startTime), options);

    GraphPath pathBr = spt1.getPath(end, false);
    assertNotNull("There must be a path from br to end", pathBr);

    ShortestPathTree spt2 =
        AStar.getShortestPathTree(graph, trOut, end, TestUtils.toSeconds(startTime), options);

    GraphPath pathTr = spt2.getPath(end, false);
    assertNotNull("There must be a path from tr to end", pathTr);
    assertTrue(
        "path from bottom to end must be longer than path from top to end",
        pathBr.getWeight() > pathTr.getWeight());

    ShortestPathTree spt =
        AStar.getShortestPathTree(graph, start, end, TestUtils.toSeconds(startTime), options);

    GraphPath path = spt.getPath(end, false);
    assertNotNull("There must be a path from start to end", path);

    // the bottom is not part of the shortest path
    for (State s : path.states) {
      assertNotSame(s.getVertex(), graph.getVertex("bottom"));
      assertNotSame(s.getVertex(), graph.getVertex("bottomBack"));
    }

    startTime = new GregorianCalendar(2009, 11, 1, 12, 34, 25);

    options.setArriveBy(true);
    spt = AStar.getShortestPathTree(graph, start, end, TestUtils.toSeconds(startTime), options);

    path = spt.getPath(start, false);
    assertNotNull("There must be a path from start to end (looking back)", path);

    // the bottom edge is not part of the shortest path
    for (State s : path.states) {
      assertNotSame(s.getVertex(), graph.getVertex("bottom"));
      assertNotSame(s.getVertex(), graph.getVertex("bottomBack"));
    }

    /* Now, the right edge is not bikeable.  But the user can walk their bike.  So here are some tests
     * that prove (a) that walking bikes works, but that (b) it is not preferred to riding a tiny bit longer.
     */

    options = new TraverseOptions(new TraverseModeSet(TraverseMode.BICYCLE));
    start =
        StreetLocation.createStreetLocation(
            "start1",
            "start1",
            cast(turns, StreetEdge.class),
            new LinearLocation(0, 0.95).getCoordinate(top.getGeometry()));
    end =
        StreetLocation.createStreetLocation(
            "end1",
            "end1",
            cast(turns, StreetEdge.class),
            new LinearLocation(0, 0.95).getCoordinate(bottom.getGeometry()));
    spt = AStar.getShortestPathTree(graph, start, end, TestUtils.toSeconds(startTime), options);

    path = spt.getPath(start, false);
    assertNotNull("There must be a path from top to bottom along the right", path);

    // the left edge is not part of the shortest path (even though the bike must be walked along the
    // right)
    for (State s : path.states) {
      assertNotSame(s.getVertex(), graph.getVertex("left"));
      assertNotSame(s.getVertex(), graph.getVertex("leftBack"));
    }

    start =
        StreetLocation.createStreetLocation(
            "start2",
            "start2",
            cast(turns, StreetEdge.class),
            new LinearLocation(0, 0.55).getCoordinate(top.getGeometry()));
    end =
        StreetLocation.createStreetLocation(
            "end2",
            "end2",
            cast(turns, StreetEdge.class),
            new LinearLocation(0, 0.55).getCoordinate(bottom.getGeometry()));
    spt = AStar.getShortestPathTree(graph, start, end, TestUtils.toSeconds(startTime), options);

    path = spt.getPath(start, false);
    assertNotNull("There must be a path from top to bottom", path);

    // the right edge is not part of the shortest path, e
    for (State s : path.states) {
      assertNotSame(s.getVertex(), graph.getVertex("right"));
      assertNotSame(s.getVertex(), graph.getVertex("rightBack"));
    }
  }
  public void testTransitIndex() throws JSONException {
    TransitIndex index = new TransitIndex();
    index.setGraphService(Context.getInstance().graphService);
    String routerId = "portland";
    AgencyList agencyIds = index.getAgencyIds(routerId);
    assertEquals(agencyIds.agencies.toArray(new Agency[0])[0].getId(), ("TriMet"));
    assertEquals(1, agencyIds.agencies.size());

    RouteDataList routeDataList =
        (RouteDataList) index.getRouteData("TriMet", "100", false, false, routerId);
    assertEquals(
        new AgencyAndId("TriMet", "100"), routeDataList.routeData.toArray(new RouteData[0])[0].id);
    assertTrue(routeDataList.routeData.toArray(new RouteData[0])[0].variants.size() >= 2);

    RouteList routes = (RouteList) index.getRoutes("TriMet", false, routerId);
    assertTrue(routes.routes.size() > 50);

    // without agencyId
    routes = (RouteList) index.getRoutes(null, true, routerId);
    assertTrue(routes.routes.size() > 50);

    // without agencyId
    routes = (RouteList) index.getRoutes(null, true, routerId);
    assertTrue(routes.routes.size() > 50);

    ModeList modes = (ModeList) index.getModes(routerId);
    assertTrue(modes.modes.contains(TraverseMode.TRAM));
    assertFalse(modes.modes.contains(TraverseMode.FUNICULAR));

    RouteList routesForStop =
        (RouteList) index.getRoutesForStop("TriMet", "10579", false, routerId);
    assertEquals(1, routesForStop.routes.size());

    routesForStop = (RouteList) index.getRoutesForStop(null, "10579", false, routerId);
    assertEquals(1, routesForStop.routes.size());
    // assertEquals("MAX Red Line", routesForStop.routes.get(0).routeLongName);

    StopList stopsNearPoint =
        (StopList) index.getStopsNearPoint("TriMet", 45.464783, -122.578918, false, routerId);
    assertTrue(stopsNearPoint.stops.size() > 0);

    long startTime = TestUtils.dateInSeconds("America/Los_Angeles", 2009, 9, 1, 7, 50, 0) * 1000;
    long endTime = startTime + 60 * 60 * 1000;
    StopTimeList stopTimesForStop =
        (StopTimeList)
            index.getStopTimesForStop(
                "TriMet", "10579", startTime, endTime, false, false, null, routerId);
    assertTrue(stopTimesForStop.stopTimes.size() > 0);

    stopTimesForStop =
        (StopTimeList)
            index.getStopTimesForStop(
                null, "10579", startTime, endTime, false, false, null, routerId);
    assertTrue(stopTimesForStop.stopTimes.size() > 0);

    stopTimesForStop =
        (StopTimeList)
            index.getStopTimesForStop(
                null, "10579", startTime, endTime, false, false, null, routerId);
    assertTrue(stopTimesForStop.stopTimes.size() > 0);

    stopTimesForStop =
        (StopTimeList)
            index.getStopTimesForStop(
                null, "10579", startTime, endTime, false, false, null, routerId);
    assertTrue(stopTimesForStop.stopTimes.size() > 0);

    // StopTimeList stopTimesForTrip = (StopTimeList) index.getStopTimesForTrip("TriMet", "1254",
    // "TriMet", "10W1040", startTime, routerId);
    // assertTrue(stopTimesForTrip.stopTimes.size() > 0);
  }
  @Test
  public void testUpdate() {
    TripUpdate tripUpdate;
    TripUpdate.Builder tripUpdateBuilder;
    TripDescriptor.Builder tripDescriptorBuilder;
    StopTimeUpdate.Builder stopTimeUpdateBuilder;
    StopTimeEvent.Builder stopTimeEventBuilder;

    int trip_1_1_index = timetable.getTripIndex(new AgencyAndId("agency", "1.1"));

    Vertex stop_a = graph.getVertex("agency_A");
    Vertex stop_c = graph.getVertex("agency_C");
    RoutingRequest options = new RoutingRequest();

    ShortestPathTree spt;
    GraphPath path;

    // non-existing trip
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("b");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.CANCELED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    tripUpdate = tripUpdateBuilder.build();
    assertFalse(timetable.update(tripUpdate, "a", timeZone, serviceDate));

    // update trip with bad data
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(0);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SKIPPED);
    tripUpdate = tripUpdateBuilder.build();
    assertFalse(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    // update trip with non-increasing data
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(2);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder();
    stopTimeEventBuilder.setTime(
        TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 10, 1));
    stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder();
    stopTimeEventBuilder.setTime(
        TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 10, 0));
    tripUpdate = tripUpdateBuilder.build();
    assertFalse(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    // ---
    long startTime = TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 0, 0);
    long endTime;
    options.dateTime = startTime;

    // ---
    options.setRoutingContext(graph, stop_a, stop_c);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_c, false);
    assertNotNull(path);
    endTime = startTime + 20 * 60;
    assertEquals(endTime, path.getEndTime());

    // update trip
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(1);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder();
    stopTimeEventBuilder.setTime(
        TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 2, 0));
    stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder();
    stopTimeEventBuilder.setTime(
        TestUtils.dateInSeconds("America/New_York", 2009, AUGUST, 7, 0, 2, 0));
    tripUpdate = tripUpdateBuilder.build();
    assertEquals(20 * 60, timetable.getTripTimes(trip_1_1_index).getArrivalTime(2));
    assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate));
    assertEquals(20 * 60 + 120, timetable.getTripTimes(trip_1_1_index).getArrivalTime(2));

    // ---
    options.setRoutingContext(graph, stop_a, stop_c);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_c, false);
    assertNotNull(path);
    endTime = startTime + 20 * 60 + 120;
    assertEquals(endTime, path.getEndTime());

    // cancel trip
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.CANCELED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    tripUpdate = tripUpdateBuilder.build();
    assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    TripTimes tripTimes = timetable.getTripTimes(trip_1_1_index);
    for (int i = 0; i < tripTimes.getNumStops(); i++) {
      assertEquals(TripTimes.UNAVAILABLE, tripTimes.getDepartureTime(i));
      assertEquals(TripTimes.UNAVAILABLE, tripTimes.getArrivalTime(i));
    }

    // ---
    options.setRoutingContext(graph, stop_a, stop_c);
    spt = aStar.getShortestPathTree(options);
    path = spt.getPath(stop_c, false);
    assertNotNull(path);
    endTime = startTime + 40 * 60;
    assertEquals(endTime, path.getEndTime());

    // update trip arrival time incorrectly
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(1);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder();
    stopTimeEventBuilder.setDelay(0);
    tripUpdate = tripUpdateBuilder.build();
    assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    // update trip arrival time only
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(2);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder();
    stopTimeEventBuilder.setDelay(1);
    tripUpdate = tripUpdateBuilder.build();
    assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    // update trip departure time only
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(2);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder();
    stopTimeEventBuilder.setDelay(-1);
    tripUpdate = tripUpdateBuilder.build();
    assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    // update trip using stop id
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopId("B");
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder();
    stopTimeEventBuilder.setDelay(-1);
    tripUpdate = tripUpdateBuilder.build();
    assertTrue(timetable.update(tripUpdate, "agency", timeZone, serviceDate));

    // update trip arrival time at first stop and make departure time incoherent at second stop
    tripDescriptorBuilder = TripDescriptor.newBuilder();
    tripDescriptorBuilder.setTripId("1.1");
    tripDescriptorBuilder.setScheduleRelationship(TripDescriptor.ScheduleRelationship.SCHEDULED);
    tripUpdateBuilder = TripUpdate.newBuilder();
    tripUpdateBuilder.setTrip(tripDescriptorBuilder);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(0);
    stopTimeUpdateBuilder.setStopSequence(1);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getArrivalBuilder();
    stopTimeEventBuilder.setDelay(0);
    stopTimeUpdateBuilder = tripUpdateBuilder.addStopTimeUpdateBuilder(1);
    stopTimeUpdateBuilder.setStopSequence(2);
    stopTimeUpdateBuilder.setScheduleRelationship(StopTimeUpdate.ScheduleRelationship.SCHEDULED);
    stopTimeEventBuilder = stopTimeUpdateBuilder.getDepartureBuilder();
    stopTimeEventBuilder.setDelay(-1);
    tripUpdate = tripUpdateBuilder.build();
    assertFalse(timetable.update(tripUpdate, "agency", timeZone, serviceDate));
  }