private void computeTimes (TransportNetwork network, ProfileRequest req, TIntIntMap accessTimes, TIntIntMap egressTimes) {
        if (!accessTimes.containsKey(this.boardStops[0])) throw new IllegalArgumentException("Access times do not contain first stop of path!");
        if (!egressTimes.containsKey(this.alightStops[this.length - 1])) throw new IllegalArgumentException("Egress times do not contain last stop of path!");
        int accessTime = accessTimes.get(this.boardStops[0]);
        int egressTime = egressTimes.get(this.alightStops[this.length - 1]);
        if (network.transitLayer.hasFrequencies) {
            // TODO fix
            throw new UnsupportedOperationException("Frequency-based trips are not yet supported in customer-facing profile routing");
        }
        // we now know what patterns are being used, interleave to find times
        // NB no need to reverse-optimize these itineraries; we'll just filter them below.
        TripPattern[] patterns = IntStream.of(this.patterns).mapToObj(p -> network.transitLayer.tripPatterns.get(p)).toArray(s -> new TripPattern[s]);
        // find all possible times to board and alight each pattern
        // for each trip pattern, for each trip on that pattern, array of [depart origin, arrive destination]
        int[][][] times = new int[patterns.length][][];
        for (int patIdx = 0; patIdx < patterns.length; patIdx++) {
            final int pidx = patIdx;
            int fromStopInPattern = 0;
            while (patterns[patIdx].stops[fromStopInPattern] != this.boardStops[pidx]) fromStopInPattern++;
            int toStopInPattern = fromStopInPattern;
            while (patterns[patIdx].stops[toStopInPattern] != this.alightStops[pidx]) {
                // if we visit the board stop multiple times, board at the one closest to the alight stop
                // TODO better handle duplicated stops/loop routes
                if (patterns[patIdx].stops[toStopInPattern] == this.boardStops[pidx]) fromStopInPattern = toStopInPattern;
                toStopInPattern++;
            }
            final int finalFromStopInPattern = fromStopInPattern;
            final int finalToStopInPattern = toStopInPattern;
            times[patIdx] = patterns[patIdx].tripSchedules.stream()
                    .map(ts -> new int[] { ts.departures[finalFromStopInPattern], ts.arrivals[finalToStopInPattern] })
                    .toArray(s -> new int[s][]);
        }
        // sort by departure time of each trip, within each pattern
        Stream.of(times).forEach(t -> Arrays.sort(t, (t1, t2) -> t1[0] - t2[0]));
        // loop over departures within the time window
        // firstTrip is the trip on the first pattern
        int firstTrip = 0;
        while (times[0][firstTrip][0] < req.fromTime + accessTime + FastRaptorWorker.BOARD_SLACK_SECONDS) firstTrip++;
        // now interleave times
        double walkSpeedMillimetersPerSecond = req.walkSpeed * 1000;
        TIMES: while (firstTrip < times[0].length) {
            Itinerary itin = new Itinerary(this.patterns.length);
            int time = times[0][firstTrip][0];
            // linear scan over timetable to do interleaving
            for (int patIdx = 0; patIdx < this.patterns.length; patIdx++) {
                int trip = 0;
                while (times[patIdx][trip][0] < time) {
                    trip++;
                    if (trip >= times[patIdx].length) break TIMES; // we've found the end of the times at which this path is possible
                }
                itin.boardTimes[patIdx] = times[patIdx][trip][0];
                itin.alightTimes[patIdx] = times[patIdx][trip][1];
                if (patIdx < this.length - 1) {
                    // find the transfer time
                    TIntList transfers = network.transitLayer.transfersForStop.get(this.alightStops[patIdx]);
                    int transferTime;
                    if (this.alightStops[patIdx] != this.boardStops[patIdx + 1]) {
                        transferTime = -1;
                        for (int i = 0; i < transfers.size(); i += 2) {
                            if (transfers.get(i) == this.boardStops[patIdx + 1]) {
                                int transferDistanceMillimeters = transfers.get(i + 1);
                                transferTime = (int)(transferDistanceMillimeters / walkSpeedMillimetersPerSecond);
                                break;
                            }
                        }
                        if (transferTime == -1) {
                            throw new IllegalStateException("Did not find transfer in transit network, indicates an internal error");
                        }
                    }
                    else transferTime = 0; // no transfer time, we are at the same stop (board slack applied below)
                    // TODO should board slack be applied at the origin stop? Is this done in RaptorWorker?
                    // See also below in computeStatistics
                    time = times[patIdx][trip][1] + transferTime + FastRaptorWorker.BOARD_SLACK_SECONDS;
                    itin.arriveAtBoardStopTimes[patIdx + 1] = time;
                }
            }
            this.itineraries.add(itin);
            firstTrip++;
        }
        sortAndFilterItineraries();
        computeStatistics(req, accessTime, egressTime);
    }
private void computeTimes (TransportNetwork network, ProfileRequest req, TIntIntMap accessTimes, TIntIntMap egressTimes) {
        if (!accessTimes.containsKey(this.boardStops[0])) throw new IllegalArgumentException("Access times do not contain first stop of path!");
        if (!egressTimes.containsKey(this.alightStops[this.length - 1])) throw new IllegalArgumentException("Egress times do not contain last stop of path!");
        int accessTime = accessTimes.get(this.boardStops[0]);
        int egressTime = egressTimes.get(this.alightStops[this.length - 1]);
        if (network.transitLayer.hasFrequencies) {
           
            throw new UnsupportedOperationException("Frequency-based trips are not yet supported in customer-facing profile routing");
        }
       
       
        TripPattern[] patterns = IntStream.of(this.patterns).mapToObj(p -> network.transitLayer.tripPatterns.get(p)).toArray(s -> new TripPattern[s]);
       
       
        int[][][] times = new int[patterns.length][][];
        for (int patIdx = 0; patIdx < patterns.length; patIdx++) {
            final int pidx = patIdx;
            int fromStopInPattern = 0;
            while (patterns[patIdx].stops[fromStopInPattern] != this.boardStops[pidx]) fromStopInPattern++;
            int toStopInPattern = fromStopInPattern;
            while (patterns[patIdx].stops[toStopInPattern] != this.alightStops[pidx]) {
               
               
                if (patterns[patIdx].stops[toStopInPattern] == this.boardStops[pidx]) fromStopInPattern = toStopInPattern;
                toStopInPattern++;
            }
            final int finalFromStopInPattern = fromStopInPattern;
            final int finalToStopInPattern = toStopInPattern;
            times[patIdx] = patterns[patIdx].tripSchedules.stream()
                    .map(ts -> new int[] { ts.departures[finalFromStopInPattern], ts.arrivals[finalToStopInPattern] })
                    .toArray(s -> new int[s][]);
        }
       
        Stream.of(times).forEach(t -> Arrays.sort(t, (t1, t2) -> t1[0] - t2[0]));
       
       
        int firstTrip = 0;
        while (times[0][firstTrip][0] < req.fromTime + accessTime + FastRaptorWorker.BOARD_SLACK_SECONDS) firstTrip++;
       
        double walkSpeedMillimetersPerSecond = req.walkSpeed * 1000;
        TIMES: while (firstTrip < times[0].length) {
            Itinerary itin = new Itinerary(this.patterns.length);
            int time = times[0][firstTrip][0];
           
            for (int patIdx = 0; patIdx < this.patterns.length; patIdx++) {
                int trip = 0;
                while (times[patIdx][trip][0] < time) {
                    trip++;
                    if (trip >= times[patIdx].length) break TIMES;
                }
                itin.boardTimes[patIdx] = times[patIdx][trip][0];
                itin.alightTimes[patIdx] = times[patIdx][trip][1];
                if (patIdx < this.length - 1) {
                   
                    TIntList transfers = network.transitLayer.transfersForStop.get(this.alightStops[patIdx]);
                    int transferTime;
                    if (this.alightStops[patIdx] != this.boardStops[patIdx + 1]) {
                        transferTime = -1;
                        for (int i = 0; i < transfers.size(); i += 2) {
                            if (transfers.get(i) == this.boardStops[patIdx + 1]) {
                                int transferDistanceMillimeters = transfers.get(i + 1);
                                transferTime = (int)(transferDistanceMillimeters / walkSpeedMillimetersPerSecond);
                                break;
                            }
                        }
                        if (transferTime == -1) {
                            throw new IllegalStateException("Did not find transfer in transit network, indicates an internal error");
                        }
                    }
                    else transferTime = 0;
                   
                   
                    time = times[patIdx][trip][1] + transferTime + FastRaptorWorker.BOARD_SLACK_SECONDS;
                    itin.arriveAtBoardStopTimes[patIdx + 1] = time;
                }
            }
            this.itineraries.add(itin);
            firstTrip++;
        }
        sortAndFilterItineraries();
        computeStatistics(req, accessTime, egressTime);
    }
private void computetimes (transportnetwork network, profilerequest req, tintintmap accesstimes, tintintmap egresstimes) { if (!accesstimes.containskey(this.boardstops[0])) throw new illegalargumentexception("access times do not contain first stop of path!"); if (!egresstimes.containskey(this.alightstops[this.length - 1])) throw new illegalargumentexception("egress times do not contain last stop of path!"); int accesstime = accesstimes.get(this.boardstops[0]); int egresstime = egresstimes.get(this.alightstops[this.length - 1]); if (network.transitlayer.hasfrequencies) { throw new unsupportedoperationexception("frequency-based trips are not yet supported in customer-facing profile routing"); } trippattern[] patterns = intstream.of(this.patterns).maptoobj(p -> network.transitlayer.trippatterns.get(p)).toarray(s -> new trippattern[s]); int[][][] times = new int[patterns.length][][]; for (int patidx = 0; patidx < patterns.length; patidx++) { final int pidx = patidx; int fromstopinpattern = 0; while (patterns[patidx].stops[fromstopinpattern] != this.boardstops[pidx]) fromstopinpattern++; int tostopinpattern = fromstopinpattern; while (patterns[patidx].stops[tostopinpattern] != this.alightstops[pidx]) { if (patterns[patidx].stops[tostopinpattern] == this.boardstops[pidx]) fromstopinpattern = tostopinpattern; tostopinpattern++; } final int finalfromstopinpattern = fromstopinpattern; final int finaltostopinpattern = tostopinpattern; times[patidx] = patterns[patidx].tripschedules.stream() .map(ts -> new int[] { ts.departures[finalfromstopinpattern], ts.arrivals[finaltostopinpattern] }) .toarray(s -> new int[s][]); } stream.of(times).foreach(t -> arrays.sort(t, (t1, t2) -> t1[0] - t2[0])); int firsttrip = 0; while (times[0][firsttrip][0] < req.fromtime + accesstime + fastraptorworker.board_slack_seconds) firsttrip++; double walkspeedmillimeterspersecond = req.walkspeed * 1000; times: while (firsttrip < times[0].length) { itinerary itin = new itinerary(this.patterns.length); int time = times[0][firsttrip][0]; for (int patidx = 0; patidx < this.patterns.length; patidx++) { int trip = 0; while (times[patidx][trip][0] < time) { trip++; if (trip >= times[patidx].length) break times; } itin.boardtimes[patidx] = times[patidx][trip][0]; itin.alighttimes[patidx] = times[patidx][trip][1]; if (patidx < this.length - 1) { tintlist transfers = network.transitlayer.transfersforstop.get(this.alightstops[patidx]); int transfertime; if (this.alightstops[patidx] != this.boardstops[patidx + 1]) { transfertime = -1; for (int i = 0; i < transfers.size(); i += 2) { if (transfers.get(i) == this.boardstops[patidx + 1]) { int transferdistancemillimeters = transfers.get(i + 1); transfertime = (int)(transferdistancemillimeters / walkspeedmillimeterspersecond); break; } } if (transfertime == -1) { throw new illegalstateexception("did not find transfer in transit network, indicates an internal error"); } } else transfertime = 0; time = times[patidx][trip][1] + transfertime + fastraptorworker.board_slack_seconds; itin.arriveatboardstoptimes[patidx + 1] = time; } } this.itineraries.add(itin); firsttrip++; } sortandfilteritineraries(); computestatistics(req, accesstime, egresstime); }