/******************************************************************************* * 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 com.andork.plot; import java.util.ArrayList; import java.util.List; import java.util.ListIterator; import java.util.RandomAccess; public class ListUtils { private static final int BINARYSEARCH_THRESHOLD = 5000; /** * Searches the specified list for the specified object using the binary * search algorithm. The list must be sorted into ascending order according * to the {@linkplain Comparable natural ordering} of its elements (as by * the {@link #sort(List)} method) prior to making this call. If it is not * sorted, the results are undefined. If the list contains multiple elements * equal to the specified object, there is no guarantee which one will be * found. * * <p> * This method runs in log(n) time for a "random access" list (which * provides near-constant-time positional access). If the specified list * does not implement the {@link RandomAccess} interface and is large, this * method will do an iterator-based binary search that performs O(n) link * traversals and O(log n) element comparisons. * * @param list * the list to be searched. * @param key * the key to be searched for. * @return the index of the search key, if it is contained in the list; * otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>. The * <i>insertion point</i> is defined as the point at which the key * would be inserted into the list: the index of the first element * greater than the key, or <tt>list.size()</tt> if all elements in * the list are less than the specified key. Note that this * guarantees that the return value will be >= 0 if and only if * the key is found. * @throws ClassCastException * if the list contains elements that are not <i>mutually * comparable</i> (for example, strings and integers), or the * search key is not mutually comparable with the elements of * the list. */ public static <T> int binarySearch(List<? extends T> list, double key, DoubleKeyGetter<T> keyGetter) { if (list instanceof RandomAccess || list.size() < BINARYSEARCH_THRESHOLD) { return indexedBinarySearch(list, key, keyGetter); } else { return iteratorBinarySearch(list, key, keyGetter); } } /** * Searches the specified list for the specified object using the binary * search algorithm. The list must be sorted into ascending order according * to the {@linkplain Comparable natural ordering} of its elements (as by * the {@link #sort(List)} method) prior to making this call. If it is not * sorted, the results are undefined. If the list contains multiple elements * equal to the specified object, there is no guarantee which one will be * found. * * <p> * This method runs in log(n) time for a "random access" list (which * provides near-constant-time positional access). If the specified list * does not implement the {@link RandomAccess} interface and is large, this * method will do an iterator-based binary search that performs O(n) link * traversals and O(log n) element comparisons. * * @param list * the list to be searched. * @param key * the key to be searched for. * @return the index of the search key, if it is contained in the list; * otherwise, <tt>(-(<i>insertion point</i>) - 1)</tt>. The * <i>insertion point</i> is defined as the point at which the key * would be inserted into the list: the index of the first element * greater than the key, or <tt>list.size()</tt> if all elements in * the list are less than the specified key. Note that this * guarantees that the return value will be >= 0 if and only if * the key is found. * @throws ClassCastException * if the list contains elements that are not <i>mutually * comparable</i> (for example, strings and integers), or the * search key is not mutually comparable with the elements of * the list. */ public static int binarySearch(List<Double> list, double key) { if (list instanceof RandomAccess || list.size() < BINARYSEARCH_THRESHOLD) { return indexedBinarySearch(list, key); } else { return iteratorBinarySearch(list, key); } } public static int ceilingIndex(List<Double> a, double key) { int i = binarySearch(a, key); if (i >= 0) { return i; } i = -(i + 1); return i == a.size() ? -1 : i; } public static <T> int ceilingIndex(List<T> a, double key, DoubleKeyGetter<T> keyGetter) { int i = binarySearch(a, key, keyGetter); if (i >= 0) { return i; } i = -(i + 1); return i == a.size() ? -1 : i; } public static int ceilingIndexUnsorted(List<Double> a, double key) { double ceiling = Double.NaN; int ceilingIndex = -1; for (int i = 0; i < a.size(); i++) { double iKey = a.get(i); if (iKey == key) { return i; } else if (iKey > key && (Double.isNaN(ceiling) || iKey < ceiling)) { ceiling = iKey; ceilingIndex = i; } } return ceilingIndex; } public static <T> int ceilingIndexUnsorted(List<T> a, double key, DoubleKeyGetter<T> keyGetter) { double ceiling = Double.NaN; int ceilingIndex = -1; for (int i = 0; i < a.size(); i++) { double iKey = keyGetter.keyOf(a.get(i)); if (iKey == key) { return i; } else if (iKey > key && (Double.isNaN(ceiling) || iKey < ceiling)) { ceiling = iKey; ceilingIndex = i; } } return ceilingIndex; } public static int floorIndex(List<Double> a, double key) { int i = binarySearch(a, key); if (i >= 0) { return i; } i = -(i + 1); return i - 1; } public static <T> int floorIndex(List<T> a, double key, DoubleKeyGetter<T> keyGetter) { int i = binarySearch(a, key, keyGetter); if (i >= 0) { return i; } i = -(i + 1); return i - 1; } public static int floorIndexUnsorted(List<Double> a, double key) { double floor = Double.NaN; int floorIndex = -1; for (int i = 0; i < a.size(); i++) { double iKey = a.get(i); if (iKey == key) { return i; } else if (iKey < key && (Double.isNaN(floor) || iKey > floor)) { floor = iKey; floorIndex = i; } } return floorIndex; } public static <T> int floorIndexUnsorted(List<T> a, double key, DoubleKeyGetter<T> keyGetter) { double floor = Double.NaN; int floorIndex = -1; for (int i = 0; i < a.size(); i++) { double iKey = keyGetter.keyOf(a.get(i)); if (iKey == key) { return i; } else if (iKey < key && (Double.isNaN(floor) || iKey > floor)) { floor = iKey; floorIndex = i; } } return floorIndex; } /** * Gets the ith element from the given list by repositioning the specified * list listIterator. */ private static <T> T get(ListIterator<? extends T> i, int index) { T obj = null; int pos = i.nextIndex(); if (pos <= index) { do { obj = i.next(); } while (pos++ < index); } else { do { obj = i.previous(); } while (--pos > index); } return obj; } public static int higherIndex(List<Double> a, double key) { int i = ceilingIndex(a, key); return i < 0 || a.get(i) > key ? i : i < a.size() - 1 ? i + 1 : -1; } public static <T> int higherIndex(List<T> a, double key, DoubleKeyGetter<T> keyGetter) { int i = ceilingIndex(a, key, keyGetter); return i < 0 || keyGetter.keyOf(a.get(i)) > key ? i : i < a.size() - 1 ? i + 1 : -1; } private static <T> int indexedBinarySearch(List<? extends T> list, double key, DoubleKeyGetter<T> keyGetter) { int low = 0; int high = list.size() - 1; while (low <= high) { int mid = low + high >>> 1; double midVal = keyGetter.keyOf(list.get(mid)); int cmp; if (midVal < key) { cmp = -1; // Neither val is NaN, thisVal is smaller } else if (midVal > key) { cmp = 1; // Neither val is NaN, thisVal is larger } else { long midBits = Double.doubleToLongBits(midVal); long keyBits = Double.doubleToLongBits(key); cmp = midBits == keyBits ? 0 : // Values are equal midBits < keyBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN) 1; // (0.0, -0.0) or (NaN, !NaN) } if (cmp < 0) { low = mid + 1; } else if (cmp > 0) { high = mid - 1; } else { return mid; // key found } } return -(low + 1); // key not found } private static int indexedBinarySearch(List<Double> list, double key) { int low = 0; int high = list.size() - 1; while (low <= high) { int mid = low + high >>> 1; double midVal = list.get(mid); int cmp; if (midVal < key) { cmp = -1; // Neither val is NaN, thisVal is smaller } else if (midVal > key) { cmp = 1; // Neither val is NaN, thisVal is larger } else { long midBits = Double.doubleToLongBits(midVal); long keyBits = Double.doubleToLongBits(key); cmp = midBits == keyBits ? 0 : // Values are equal midBits < keyBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN) 1; // (0.0, -0.0) or (NaN, !NaN) } if (cmp < 0) { low = mid + 1; } else if (cmp > 0) { high = mid - 1; } else { return mid; // key found } } return -(low + 1); // key not found } private static <T> int iteratorBinarySearch(List<? extends T> list, double key, DoubleKeyGetter<T> keyGetter) { int low = 0; int high = list.size() - 1; ListIterator<? extends T> i = list.listIterator(); while (low <= high) { int mid = low + high >>> 1; double midVal = keyGetter.keyOf(get(i, mid)); int cmp; if (midVal < key) { cmp = -1; // Neither val is NaN, thisVal is smaller } else if (midVal > key) { cmp = 1; // Neither val is NaN, thisVal is larger } else { long midBits = Double.doubleToLongBits(midVal); long keyBits = Double.doubleToLongBits(key); cmp = midBits == keyBits ? 0 : // Values are equal midBits < keyBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN) 1; // (0.0, -0.0) or (NaN, !NaN) } if (cmp < 0) { low = mid + 1; } else if (cmp > 0) { high = mid - 1; } else { return mid; // key found } } return -(low + 1); // key not found } private static int iteratorBinarySearch(List<Double> list, double key) { int low = 0; int high = list.size() - 1; ListIterator<Double> i = list.listIterator(); while (low <= high) { int mid = low + high >>> 1; double midVal = get(i, mid); int cmp; if (midVal < key) { cmp = -1; // Neither val is NaN, thisVal is smaller } else if (midVal > key) { cmp = 1; // Neither val is NaN, thisVal is larger } else { long midBits = Double.doubleToLongBits(midVal); long keyBits = Double.doubleToLongBits(key); cmp = midBits == keyBits ? 0 : // Values are equal midBits < keyBits ? -1 : // (-0.0, 0.0) or (!NaN, NaN) 1; // (0.0, -0.0) or (NaN, !NaN) } if (cmp < 0) { low = mid + 1; } else if (cmp > 0) { high = mid - 1; } else { return mid; // key found } } return -(low + 1); // key not found } public static int lowerIndex(List<Double> a, double key) { int i = floorIndex(a, key); return i < 0 || a.get(i) < key ? i : i - 1; } public static <T> int lowerIndex(List<T> a, double key, DoubleKeyGetter<T> keyGetter) { int i = floorIndex(a, key, keyGetter); return i < 0 || keyGetter.keyOf(a.get(i)) < key ? i : i - 1; } public static int nearestIndex(List<Double> a, double key) { int floor = floorIndex(a, key); int ceiling = ceilingIndex(a, key); if (floor >= 0) { return ceiling < 0 || a.get(ceiling) - key > key - a.get(floor) ? floor : ceiling; } return ceiling; } public static <T> int nearestIndex(List<T> a, double key, DoubleKeyGetter<T> keyGetter) { int floor = floorIndex(a, key, keyGetter); int ceiling = ceilingIndex(a, key, keyGetter); if (floor >= 0) { return ceiling < 0 || keyGetter.keyOf(a.get(ceiling)) - key > key - keyGetter.keyOf(a.get(floor)) ? floor : ceiling; } return ceiling; } public static int nearestIndexUnsorted(List<Double> a, double key) { double nearest = Double.NaN; int nearestIndex = -1; for (int i = 0; i < a.size(); i++) { double iKey = a.get(i); if (iKey == key) { return i; } else if (Double.isNaN(nearest) || Math.abs(iKey - key) < Math.abs(nearest - key)) { nearest = iKey; nearestIndex = i; } } return nearestIndex; } public static <T> int nearestIndexUnsorted(List<T> a, double key, DoubleKeyGetter<T> keyGetter) { double nearest = Double.NaN; int nearestIndex = -1; for (int i = 0; i < a.size(); i++) { double iKey = keyGetter.keyOf(a.get(i)); if (iKey == key) { return i; } else if (Double.isNaN(nearest) || Math.abs(iKey - key) < Math.abs(nearest - key)) { nearest = iKey; nearestIndex = i; } } return nearestIndex; } public static double[] toArray(List<Double> a) { double[] result = new double[a.size()]; for (int i = 0; i < a.size(); i++) { result[i] = a.get(i); } return result; } public static ArrayList<Double> toArrayList(double[] a) { ArrayList<Double> result = new ArrayList<Double>(); for (double d : a) { result.add(d); } return result; } }