<commit_before>package com.conveyal.gtfs.validator;

import com.conveyal.gtfs.GTFSFeed;
import com.conveyal.gtfs.error.OverlappingTripsInBlockError;
import com.conveyal.gtfs.model.CalendarDate;
import com.conveyal.gtfs.model.StopTime;
import com.conveyal.gtfs.model.Trip;
import com.conveyal.gtfs.validator.model.InvalidValue;
import com.conveyal.gtfs.validator.model.Priority;
import com.conveyal.gtfs.validator.model.ValidationResult;
import com.google.common.collect.Iterables;
import org.mapdb.Fun;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by landon on 5/2/16.
 */
public class OverlappingTripsValidator extends GTFSValidator {
    private static Double distanceMultiplier = 1.0;

    public boolean validate(GTFSFeed feed, boolean repair, Double distanceMultiplier) {
        this.distanceMultiplier = distanceMultiplier;
        return validate(feed, repair);
    }

    @Override
    public boolean validate(GTFSFeed feed, boolean repair) {

        ValidationResult result = new ValidationResult();

        // check for overlapping trips within block
        HashMap<String, ArrayList<BlockInterval>> blockIntervals = new HashMap<String, ArrayList<BlockInterval>>();

        feed.trips.values().stream().iterator().forEachRemaining(trip -> {
            // store trip intervals by block id
            if (trip.block_id != null) {
                Iterable<StopTime> stopTimes = feed.getOrderedStopTimesForTrip(trip.trip_id);
                BlockInterval blockInterval = new BlockInterval();
                blockInterval.trip = trip;
                StopTime firstStopTime = Iterables.get(stopTimes, 0);
                blockInterval.startTime = firstStopTime.departure_time;
                blockInterval.firstStop = firstStopTime;
                blockInterval.lastStop = Iterables.getLast(stopTimes);

                if(!blockIntervals.containsKey(trip.block_id))
                    blockIntervals.put(trip.block_id, new ArrayList<BlockInterval>());

                blockIntervals.get(trip.block_id).add(blockInterval);

            }
        });

        for(String blockId : blockIntervals.keySet()) {

            ArrayList<BlockInterval> intervals = blockIntervals.get(blockId);

            Collections.sort(intervals, new BlockIntervalComparator());

            int iOffset = 0;
            for(BlockInterval i1 : intervals) {
                for(BlockInterval i2 : intervals.subList(iOffset, intervals.size() - 1)) {


                    String tripId1 = i1.trip.trip_id;
                    String tripId2 = i2.trip.trip_id;


                    if(!tripId1.equals(tripId2)) {
                        // if trips don't overlap, skip
                        if(i1.lastStop.departure_time <= i2.firstStop.arrival_time || i2.lastStop.departure_time <= i1.firstStop.arrival_time)
                            continue;

                        // if trips have same service id they overlap
                        if(i1.trip.service_id.equals(i2.trip.service_id)) {
                            String[] tripIds = {tripId1, tripId2};
                            try {
                                feed.errors.add(new OverlappingTripsInBlockError(0, "block_id", blockId, feed.routes.get(i1.trip.route_id), tripIds, Priority.HIGH));
                            } catch (Exception e) {

                            }
                        }

                        else {

                            // if trips don't share service id check to see if service dates fall on the same days/day of week
                            for(Map.Entry<LocalDate, CalendarDate> d1 : feed.services.get(i1.trip.service_id).calendar_dates.entrySet()) {

                                if(feed.services.get(i2.trip.service_id).calendar_dates.containsKey(d1.getKey())) {
                                    String[] tripIds = {tripId1, tripId2};
                                    try {
                                        feed.errors.add(new OverlappingTripsInBlockError(0, "block_id", blockId, feed.routes.get(i1.trip.route_id), tripIds, Priority.HIGH));
                                    } catch (Exception e) {

                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    private class BlockInterval implements Comparable<BlockInterval> {
        Trip trip;
        Integer startTime;
        StopTime firstStop;
        StopTime lastStop;
<commit_msg>fix(validator): fix bug with overlapping trips validator<commit_after>package com.conveyal.gtfs.validator;

import com.conveyal.gtfs.GTFSFeed;
import com.conveyal.gtfs.error.OverlappingTripsInBlockError;
import com.conveyal.gtfs.model.CalendarDate;
import com.conveyal.gtfs.model.Service;
import com.conveyal.gtfs.model.StopTime;
import com.conveyal.gtfs.model.Trip;
import com.conveyal.gtfs.validator.model.InvalidValue;
import com.conveyal.gtfs.validator.model.Priority;
import com.conveyal.gtfs.validator.model.ValidationResult;
import com.google.common.collect.Iterables;
import org.mapdb.Fun;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * Created by landon on 5/2/16.
 */
public class OverlappingTripsValidator extends GTFSValidator {
    private static Double distanceMultiplier = 1.0;

    public boolean validate(GTFSFeed feed, boolean repair, Double distanceMultiplier) {
        this.distanceMultiplier = distanceMultiplier;
        return validate(feed, repair);
    }

    @Override
    public boolean validate(GTFSFeed feed, boolean repair) {

        ValidationResult result = new ValidationResult();

        // check for overlapping trips within block
        HashMap<String, ArrayList<BlockInterval>> blockIntervals = new HashMap<String, ArrayList<BlockInterval>>();

        feed.trips.values().stream().iterator().forEachRemaining(trip -> {
            // store trip intervals by block id
            if (trip.block_id != null) {
                Iterable<StopTime> stopTimes = feed.getOrderedStopTimesForTrip(trip.trip_id);
                BlockInterval blockInterval = new BlockInterval();
                blockInterval.trip = trip;
                StopTime firstStopTime = Iterables.get(stopTimes, 0);
                blockInterval.startTime = firstStopTime.departure_time;
                blockInterval.firstStop = firstStopTime;
                blockInterval.lastStop = Iterables.getLast(stopTimes);

                if(!blockIntervals.containsKey(trip.block_id))
                    blockIntervals.put(trip.block_id, new ArrayList<BlockInterval>());

                blockIntervals.get(trip.block_id).add(blockInterval);

            }
        });

        for(String blockId : blockIntervals.keySet()) {

            ArrayList<BlockInterval> intervals = blockIntervals.get(blockId);

            Collections.sort(intervals, new BlockIntervalComparator());

            int iOffset = 0;
            for(BlockInterval i1 : intervals) {
                for(BlockInterval i2 : intervals.subList(iOffset, intervals.size() - 1)) {


                    String tripId1 = i1.trip.trip_id;
                    String tripId2 = i2.trip.trip_id;


                    if(!tripId1.equals(tripId2)) {
                        // if trips don't overlap, skip
                        if(i1.lastStop.departure_time <= i2.firstStop.arrival_time || i2.lastStop.departure_time <= i1.firstStop.arrival_time)
                            continue;

                        // if trips have same service id they overlap
                        if(i1.trip.service_id.equals(i2.trip.service_id)) {
                            String[] tripIds = {tripId1, tripId2};
                            try {
                                feed.errors.add(new OverlappingTripsInBlockError(0, "block_id", blockId, feed.routes.get(i1.trip.route_id), tripIds, Priority.HIGH));
                            } catch (Exception e) {

                            }
                        }

                        else {
                            Service s1 = feed.services.get(i1.trip.service_id);
                            Service s2 = feed.services.get(i2.trip.service_id);
                            // if trips don't share service id check to see if service dates fall on the same days/day of week
                            for(Map.Entry<LocalDate, CalendarDate> d1 : s1.calendar_dates.entrySet()) {
                                LocalDate date = d1.getKey();
                                if (s1.activeOn(date) && s2.activeOn(date)) {
                                    String[] tripIds = {tripId1, tripId2};
                                    try {
                                        feed.errors.add(new OverlappingTripsInBlockError(0, "block_id", blockId, feed.routes.get(i1.trip.route_id), tripIds, Priority.HIGH));
                                    } catch (Exception e) {

                                    }
                                    break;
                                }
                            }
                        }
                    }
                }
            }
        }
        return false;
    }

    private class BlockInterval implements Comparable<BlockInterval> {
        Trip trip;
        Integer startTime;
        StopTime firstStop;
        StopTime lastStop;

{'fraction_non_alphanumeric': 0.08974886784685056, 'fraction_numerical': 0.007719226018937835, 'mean_word_length': 1.757377979568672, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 23, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 13, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2278474', 'n_tokens_mistral': 2658, 'n_tokens_neox': 2508, 'n_words': 495}