/******************************************************************************* * 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.Arrays; public class ArrayUtils { /** * Adapted from {@link Arrays#binarySearch(double[], int, int, double)}. */ public static <T> int binarySearch(T[] a, int fromIndex, int toIndex, double key, DoubleKeyGetter<T> keyGetter) { rangeCheck(a.length, fromIndex, toIndex); return binarySearch0(a, fromIndex, toIndex, key, keyGetter); } private static <T> int binarySearch0(T[] a, int fromIndex, int toIndex, double key, DoubleKeyGetter<T> keyGetter) { int low = fromIndex; int high = toIndex - 1; while (low <= high) { int mid = low + high >>> 1; double midVal = keyGetter.keyOf(a[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 ceilingIndex(double[] a, double key) { int i = Arrays.binarySearch(a, key); if (i >= 0) { return i; } i = -(i + 1); return i == a.length ? -1 : i; } public static <T> int ceilingIndex(T[] a, double key, DoubleKeyGetter<T> keyGetter) { int i = binarySearch(a, 0, a.length, key, keyGetter); if (i >= 0) { return i; } i = -(i + 1); return i == a.length ? -1 : i; } public static int ceilingIndexUnsorted(double[] a, double key) { double ceiling = Double.NaN; int ceilingIndex = -1; for (int i = 0; i < a.length; i++) { if (a[i] == key) { return i; } else if (a[i] > key && (Double.isNaN(ceiling) || a[i] < ceiling)) { ceiling = a[i]; ceilingIndex = i; } } return ceilingIndex; } public static <T> int ceilingIndexUnsorted(T[] a, double key, DoubleKeyGetter<T> keyGetter) { double ceiling = Double.NaN; int ceilingIndex = -1; for (int i = 0; i < a.length; i++) { double iKey = keyGetter.keyOf(a[i]); if (iKey == key) { return i; } else if (iKey > key && (Double.isNaN(ceiling) || iKey < ceiling)) { ceiling = iKey; ceilingIndex = i; } } return ceilingIndex; } public static boolean equals(Object o1, Object o2) { return o1 == null && o2 == null || o1 != null && o1.equals(o2) || o2 != null && o2.equals(o1); } public static int floorIndex(double[] a, double key) { int i = Arrays.binarySearch(a, key); if (i >= 0) { return i; } i = -(i + 1); return i - 1; } public static <T> int floorIndex(T[] a, double key, DoubleKeyGetter<T> keyGetter) { int i = binarySearch(a, 0, a.length, key, keyGetter); if (i >= 0) { return i; } i = -(i + 1); return i - 1; } public static int floorIndexUnsorted(double[] a, double key) { double floor = Double.NaN; int floorIndex = -1; for (int i = 0; i < a.length; i++) { if (a[i] == key) { return i; } else if (a[i] < key && (Double.isNaN(floor) || a[i] > floor)) { floor = a[i]; floorIndex = i; } } return floorIndex; } public static <T> int floorIndexUnsorted(T[] a, double key, DoubleKeyGetter<T> keyGetter) { double floor = Double.NaN; int floorIndex = -1; for (int i = 0; i < a.length; i++) { double iKey = keyGetter.keyOf(a[i]); if (iKey == key) { return i; } else if (iKey < key && (Double.isNaN(floor) || iKey > floor)) { floor = iKey; floorIndex = i; } } return floorIndex; } public static int higherIndex(double[] a, double key) { int i = ceilingIndex(a, key); return i < 0 || a[i] > key ? i : i < a.length - 1 ? i + 1 : -1; } public static int higherIndex(double[] a, double key, boolean backward) { return backward ? lowerIndex(a, key) : higherIndex(a, key); } public static <T> int higherIndex(T[] a, double key, DoubleKeyGetter<T> keyGetter) { int i = ceilingIndex(a, key, keyGetter); return i < 0 || keyGetter.keyOf(a[i]) > key ? i : i < a.length - 1 ? i + 1 : -1; } public static double[] insert(double[] sortedSet, double key) { int insertIndex = ArrayUtils.ceilingIndex(sortedSet, key); if (insertIndex < 0) { insertIndex = sortedSet.length; } else if (sortedSet[insertIndex] == key) { return sortedSet; } double[] newSet = Arrays.copyOf(sortedSet, sortedSet.length + 1); System.arraycopy(sortedSet, 0, newSet, 0, insertIndex); sortedSet[insertIndex] = key; System.arraycopy(sortedSet, insertIndex, newSet, insertIndex + 1, sortedSet.length - insertIndex); return newSet; } public static double[] insert(double[] array, double key, int index) { double[] newArray = Arrays.copyOf(array, array.length + 1); System.arraycopy(array, 0, newArray, 0, index); newArray[index] = key; System.arraycopy(array, index, newArray, index + 1, array.length - index); return newArray; } public static <T> T[] insert(T[] array, T key, int index) { T[] newArray = Arrays.copyOf(array, array.length + 1); System.arraycopy(array, 0, newArray, 0, index); newArray[index] = key; System.arraycopy(array, index, newArray, index + 1, array.length - index); return newArray; } public static int lowerIndex(double[] a, double key) { int i = floorIndex(a, key); return i < 0 || a[i] < key ? i : i - 1; } public static int lowerIndex(double[] a, double key, boolean backward) { return backward ? higherIndex(a, key) : lowerIndex(a, key); } public static <T> int lowerIndex(T[] a, double key, DoubleKeyGetter<T> keyGetter) { int i = floorIndex(a, key, keyGetter); return i < 0 || keyGetter.keyOf(a[i]) < key ? i : i - 1; } public static double maxValue(double[] values) { double max = Double.NaN; for (double d : values) { if (Double.isNaN(max) || d > max) { max = d; } } return max; } public static double minValue(double[] values) { double min = Double.NaN; for (double d : values) { if (Double.isNaN(min) || d < min) { min = d; } } return min; } public static int nearestIndex(double[] a, double key) { int floor = floorIndex(a, key); int ceiling = ceilingIndex(a, key); if (floor >= 0) { return ceiling < 0 || a[ceiling] - key > key - a[floor] ? floor : ceiling; } return ceiling; } public static <T> int nearestIndex(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[ceiling]) - key > key - keyGetter.keyOf(a[floor]) ? floor : ceiling; } return ceiling; } public static int nearestIndexUnsorted(double[] a, double key) { double nearest = Double.NaN; int nearestIndex = -1; for (int i = 0; i < a.length; i++) { if (a[i] == key) { return i; } else if (Double.isNaN(nearest) || Math.abs(a[i] - key) < Math.abs(nearest - key)) { nearest = a[i]; nearestIndex = i; } } return nearestIndex; } public static <T> int nearestIndexUnsorted(T[] a, double key, DoubleKeyGetter<T> keyGetter) { double nearest = Double.NaN; int nearestIndex = -1; for (int i = 0; i < a.length; i++) { double iKey = keyGetter.keyOf(a[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; } private static void rangeCheck(int arrayLen, int fromIndex, int toIndex) { if (fromIndex > toIndex) { throw new IllegalArgumentException("fromIndex(" + fromIndex + ") > toIndex(" + toIndex + ")"); } if (fromIndex < 0) { throw new ArrayIndexOutOfBoundsException(fromIndex); } if (toIndex > arrayLen) { throw new ArrayIndexOutOfBoundsException(toIndex); } } public static double[] remove(double[] array, int index) { double[] newArray = new double[array.length - 1]; System.arraycopy(array, 0, newArray, 0, index); if (index < array.length - 1) { System.arraycopy(array, index + 1, newArray, index, array.length - index - 1); } return newArray; } @SuppressWarnings("unchecked") public static <T> T[] remove(T[] array, int index) { T[] newArray = (T[]) new Object[array.length - 1]; System.arraycopy(array, 0, newArray, 0, index); if (index < array.length - 1) { System.arraycopy(array, index + 1, newArray, index, array.length - index - 1); } return newArray; } }