/*******************************************************************************
* Breakout Cave Survey Visualizer
*
* Copyright (C) 2014 James Edwards
*
* jedwards8 at fastmail dot fm
*
* This program is free software; you can redistribute it and/or modify it under
* the terms of the GNU General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option) any later
* version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
* details.
*
* You should have received a copy of the GNU General Public License along with
* this program; if not, write to the Free Software Foundation, Inc., 51
* Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*******************************************************************************/
package org.andork.collect;
import java.util.AbstractSequentialList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
/**
* Iterates through a SORTED list, returning elements that are closer to a fixed
* value first.
*
* @author andy.edwards
*
* @param <E>
* the list element type.
*/
public class NearestFirstIterator<E extends Comparable<E>> implements Iterator<E> {
public static class DoubleNearnessComparator implements NearnessComparator<Double> {
public static final DoubleNearnessComparator INSTANCE = new DoubleNearnessComparator();
private DoubleNearnessComparator() {
}
@Override
public int compare(Double lo, Double mid, Double hi) {
return Double.compare(mid - lo, hi - mid);
}
}
/**
* Determines which of two values is closer to a mid value.
*
* @author andy.edwards
*
* @param <E>
* the value type.
*/
public static interface NearnessComparator<E extends Comparable<E>> {
/**
* Determines which of two values is closer to a mid value.
*
* @param lo
* the low value; {@code lo <= mid <= hi}
* @param mid
* the mid value; {@code lo <= mid <= hi}
* @param hi
* the high value; {@code lo <= mid <= hi}
* @return {@code < 0} if {@code lo} is closer to {@code mid} than
* {@code hi} is to {@code mid}; {@code > 0} if {@code hi} is
* closer; and {@code 0} if they are equally close.
*/
public int compare(E lo, E mid, E hi);
}
/**
* Creates an {@link Iterable} that will return a
* {@link NearestFirstIterator}.
*
* @param list
* the list to iterate over. MUST BE SORTED in ascending order,
* or undefined behavior will result!
* @param midValue
* elements that are closer to this value will be returned by
* {@link #next()} first.
* @return an {@link Iterable} whose {@link Iterable#iterator() iterator()}
* method will return a {@link NearestFirstIterator}.
*/
public static Iterable<Double> iterable(final List<Double> list, final double midValue) {
return new Iterable<Double>() {
@Override
public Iterator<Double> iterator() {
return new NearestFirstIterator<Double>(list, midValue, DoubleNearnessComparator.INSTANCE);
}
};
}
/**
* Creates an {@link Iterable} that will return a
* {@link NearestFirstIterator}.
*
* @param list
* the list to iterate over. MUST BE SORTED in ascending order,
* or undefined behavior will result!
* @param midValue
* elements that are closer to this value will be returned by
* {@link #next()} first.
* @param comparator
* used to determine the next closest element to {@code midValue}
* .
* @return an {@link Iterable} whose {@link Iterable#iterator() iterator()}
* method will return a {@link NearestFirstIterator}.
*/
public static <E extends Comparable<E>> Iterable<E> iterable(final List<E> list, final E midValue,
final NearnessComparator<E> comparator) {
return new Iterable<E>() {
@Override
public Iterator<E> iterator() {
return new NearestFirstIterator<E>(list, midValue, comparator);
}
};
}
private NearnessComparator<E> comparator;
private ListIterator<E> lo;
private ListIterator<E> hi;
private E midValue;
/**
* Creates a {@link NearestFirstIterator} that will iterate over the given
* list.
*
* @param list
* the list to iterate over. MUST BE SORTED in ascending order,
* or undefined behavior will result!
* @param midValue
* elements that are closer to this value will be returned by
* {@link #next()} first.
* @param comparator
* used to determine the next closest element to {@code midValue}
* .
*/
public NearestFirstIterator(List<E> list, E midValue, NearnessComparator<E> comparator) {
this.comparator = comparator;
this.midValue = midValue;
if (list instanceof AbstractSequentialList) {
hi = list.listIterator();
lo = list.listIterator();
while (hi.hasNext()) {
if (hi.next().compareTo(midValue) >= 0) {
hi.previous();
break;
}
lo.next();
}
} else {
int hiIndex = Collections.binarySearch(list, midValue);
if (hiIndex < 0) {
hiIndex = -(hiIndex + 1);
}
hi = list.listIterator(hiIndex);
lo = list.listIterator(hiIndex);
}
}
@Override
public boolean hasNext() {
return hi.hasNext() || lo.hasPrevious();
}
@Override
public E next() {
if (!hasNext()) {
throw new IllegalStateException("The end has already been reached");
}
if (lo.hasPrevious() && hi.hasNext()) {
E loValue = lo.previous();
E hiValue = hi.next();
if (comparator.compare(loValue, midValue, hiValue) < 0) {
hi.previous();
return loValue;
} else {
lo.next();
return hiValue;
}
} else if (lo.hasPrevious()) {
return lo.previous();
} else {
return hi.next();
}
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
}