public static <T> NavigableSet<Point<T>> slowKNearestPoints(Collection<? extends Point<T>> points, Instant time, int k) {
        checkNotNull(points, "The input collection of Points cannot be null");
        checkNotNull(time, "The input time cannot be null");
        checkArgument(k >= 0, "k (" + k + ") must be non-negative");

        if (k >= points.size()) {
            return newTreeSet(points);
        }
        if (k == 0) {
            return newTreeSet();
        }

        TreeSet<Point<T>> bestSoFar = new TreeSet<>();
        Iterator<? extends Point<T>> iter = points.iterator();

        //seed with k pieces of data
        while (bestSoFar.size() < k) {
            bestSoFar.add(iter.next());
        }

        //the "next point" must be closer than this to go into the working solution
        Duration upperDelta = durationBtw(time, bestSoFar.first().time());
        Duration lowerDelta = durationBtw(time, bestSoFar.last().time());
        Duration addThreshold = max(upperDelta, lowerDelta);

        while (iter.hasNext()) {
            Point<T> next = iter.next();

            Duration delta = durationBtw(time, next.time());

            if (theDuration(delta).isLessThan(addThreshold)) {
                /* This element improves the working result. So Add it */
                bestSoFar.add(next);
                /* Recompute the upper and lower thresholds so we know which Point gets removed. */
                upperDelta = durationBtw(time, bestSoFar.first().time());
                lowerDelta = durationBtw(time, bestSoFar.last().time());

                //remove the k+1 element
                if (theDuration(upperDelta).isGreaterThanOrEqualTo(lowerDelta)) {
                    bestSoFar.pollFirst();
                    upperDelta = durationBtw(time, bestSoFar.first().time());
                } else {
                    bestSoFar.pollLast();
                    lowerDelta = durationBtw(time, bestSoFar.last().time());
                }
                addThreshold = max(upperDelta, lowerDelta);
            }
        }

        return newTreeSet(bestSoFar);
    }
@Test
    public void testKNearestPoints() {

        //this
        File file = getResourceFile("Track1.txt");

        if (file == null) {
            fail();
        }

        try {
            //read a File, convert each Line to a Point and collect the results in a list
            List<Point<NopHit>> points = Files.lines(file.toPath())
                .map((String s) -> NopHit.from(s))
                .collect(Collectors.toList());

            shuffle(points);

            NavigableSet<Point<NopHit>> kNN = slowKNearestPoints(
                points,
                parseNopTime("07/08/2017", "14:11:59.454"),
                5
            );

            confirmNopEquality(
                kNN,
                "[RH],STARS,GEG,07/08/2017,14:11:50.254,,,,1200,38,132,264,47.5884,-117.65535,655,0,-4.246,-1.8789,,,,GEG,,,,,,,IFR,,,,,,,,,,,,{RH}",
                "[RH],STARS,GEG,07/08/2017,14:11:55.044,,,,1200,39,134,250,47.58755,-117.65948,655,0,-4.4141,-1.9297,,,,GEG,,,,,,,IFR,,,,,,,,,,,,{RH}",
                "[RH],STARS,GEG,07/08/2017,14:11:59.454,,,,1200,39,132,257,47.58689,-117.66352,655,0,-4.5782,-1.9688,,,,GEG,,,,,,,IFR,,,,,,,,,,,,{RH}",
                "[RH],STARS,GEG,07/08/2017,14:12:04.134,,,,1200,39,133,246,47.58585,-117.66736,655,0,-4.7343,-2.0313,,,,GEG,,,,,,,IFR,,,,,,,,,,,,{RH}",
                "[RH],STARS,GEG,07/08/2017,14:12:08.734,,,,1200,39,133,248,47.58448,-117.67102,655,0,-4.8828,-2.1132,,,,GEG,,,,,,,IFR,,,,,,,,,,,,{RH}"
            );

        } catch (Exception ex) {
            ex.printStackTrace();
            fail("exception occured " + ex.getMessage());
        }
    }