/*
* Licensed under the Apache License, Version 2.0 (the "License");
*
* You may not use this file except in compliance with the License.
*
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*
* See the License for the specific language governing permissions and
* limitations under the License.
*
* Contributions from 2013-2017 where performed either by US government
* employees, or under US Veterans Health Administration contracts.
*
* US Veterans Health Administration contributions by government employees
* are work of the U.S. Government and are not subject to copyright
* protection in the United States. Portions contributed by government
* employees are USGovWork (17USC ยง105). Not subject to copyright.
*
* Contribution by contractors to the US Veterans Health Administration
* during this period are contractually contributed under the
* Apache License, Version 2.0.
*
* See: https://www.usa.gov/government-works
*
* Contributions prior to 2013:
*
* Copyright (C) International Health Terminology Standards Development Organisation.
* Licensed under the Apache License, Version 2.0.
*
*/
package sh.isaac.api.collections.uuidnidmap;
//~--- JDK imports ------------------------------------------------------------
import java.util.Comparator;
//~--- classes ----------------------------------------------------------------
/**
* The Class UuidSorting.
*
* @author kec
*/
public class UuidSorting
extends Object {
/** The Constant SMALL. */
private static final int SMALL = 7;
/** The Constant MEDIUM. */
private static final int MEDIUM = 40;
//~--- constructors --------------------------------------------------------
/**
* Makes this class non instantiable, but still let's others inherit from it.
*/
protected UuidSorting() {}
//~--- methods -------------------------------------------------------------
/**
* Searches the list for the specified value using the binary search algorithm. The list must
* <strong>must</strong> be sorted (as by the sort method) prior to making this call. If it is not sorted,
* the results are undefined: in particular, the call may enter an infinite loop. If the list contains
* multiple elements equal to the specified key, there is no guarantee which of the multiple elements will
* be found.
*
* @param list the list to be searched.
* @param key the value to be searched for.
* @param from the leftmost search position, inclusive.
* @param to the rightmost search position, inclusive.
* @return index of the search key, if it is contained in the list; otherwise, {@code (-(<i>insertion
* point</i>) - 1)}. The <i>insertion point</i> is defined as the the point at which the value would
* be inserted into the list: the index of the first element greater than the key, or
* {@code list.length}, 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.
* @see java.util.Arrays
*/
public static int binarySearchFromTo(long[] list, long[] key, int from, int to) {
final long[] midVal = new long[2];
while (from <= to) {
final int mid = (from + to) / 2;
midVal[0] = list[mid * 2];
midVal[1] = list[mid * 2 + 1];
if ((midVal[0] < key[0]) || ((midVal[0] == key[0]) && (midVal[1] < key[1]))) {
from = mid + 1;
} else if ((midVal[0] > key[0]) || ((midVal[0] == key[0]) && (midVal[1] > key[1]))) {
to = mid - 1;
} else {
return mid;
} // key found
}
return -(from + 1); // key not found.
}
/**
* Sorts the specified range of the specified array of elements.
*
* <p> This sort is guaranteed to be <i>stable</i>: equal elements will not be reordered as a result of
* the sort. <p>
*
* The sorting algorithm is a modified mergesort (in which the merge is omitted if the highest element in
* the low sublist is less than the lowest element in the high sublist). This algorithm offers guaranteed
* n*log(n) performance, and can approach linear performance on nearly sorted lists.
*
* @param a the array to be sorted.
* @param fromIndex the index of the first element (inclusive) to be sorted.
* @param toIndex the index of the last element (exclusive) to be sorted.
* @throws IllegalArgumentException if {@code fromIndex > toIndex}
* @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or {@code toIndex > a.length}
*/
public static void mergeSort(long[] a, int fromIndex, int toIndex) {
rangeCheck(a.length, fromIndex, toIndex);
final long aux[] = a.clone();
mergeSort1(aux, a, fromIndex, toIndex);
}
/**
* Sorts the specified range of the specified array of elements according to the order induced by the
* specified comparator. All elements in the range must be <i>mutually comparable</i> by the specified
* comparator (that is, {@code c.compare(e1, e2)} must not throw a {@code ClassCastException} for any
* elements {@code e1} and {@code e2} in the range). <p>
*
* This sort is guaranteed to be <i>stable</i>: equal elements will not be reordered as a result of the
* sort. <p>
*
* The sorting algorithm is a modified mergesort (in which the merge is omitted if the highest element in
* the low sublist is less than the lowest element in the high sublist). This algorithm offers guaranteed
* n*log(n) performance, and can approach linear performance on nearly sorted lists.
*
* @param a the array to be sorted.
* @param fromIndex the index of the first element (inclusive) to be sorted.
* @param toIndex the index of the last element (exclusive) to be sorted.
* @param c the comparator to determine the order of the array.
* @throws ClassCastException if the array contains elements that are not <i>mutually comparable</i> using
* the specified comparator.
* @throws IllegalArgumentException if {@code fromIndex > toIndex}
* @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or {@code toIndex > a.length}
* @see Comparator
*/
public static void mergeSort(long[] a, int fromIndex, int toIndex, UuidComparatorBI c) {
rangeCheck(a.length, fromIndex, toIndex);
final long aux[] = a.clone();
mergeSort1(aux, a, fromIndex, toIndex, c);
}
/**
* Sorts the specified range of the specified array of elements according to the order induced by the
* specified comparator. All elements in the range must be <i>mutually comparable</i> by the specified
* comparator (that is, {@code c.compare(e1, e2)} must not throw a {@code ClassCastException} for any
* elements {@code e1} and {@code e2} in the range). <p>
*
* The sorting algorithm is a tuned quicksort, adapted from Jon L. Bentley and M. Douglas McIlroy's
* "Engineering a Sort Function", Software-Practice and Experience, Vol. 23(11) P. 1249-1265 (November
* 1993). This algorithm offers n*log(n) performance on many data sets that cause other quicksorts to
* degrade to quadratic performance.
*
* @param a the array to be sorted.
* @param fromIndex the index of the first element (inclusive) to be sorted.
* @param toIndex the index of the last element (exclusive) to be sorted.
* @param c the comparator to determine the order of the array.
* @throws ClassCastException if the array contains elements that are not <i>mutually comparable</i> using
* the specified comparator.
* @throws IllegalArgumentException if {@code fromIndex > toIndex}
* @throws ArrayIndexOutOfBoundsException if {@code fromIndex < 0} or {@code toIndex > a.length}
* @see Comparator
*/
public static void quickSort(long[] a, int fromIndex, int toIndex, UuidComparatorBI c) {
rangeCheck(a.length, fromIndex, toIndex);
quickSort1(a, fromIndex, toIndex - fromIndex, c);
}
/**
* Returns the index of the median of the three indexed chars.
*
* @param x the x
* @param a the a
* @param b the b
* @param c the c
* @param comp the comp
* @return the int
*/
private static int med3(long x[], int a, int b, int c, UuidComparatorBI comp) {
final int ab = comp.compare(x[2 * a], x[2 * a + 1], x[2 * b], x[2 * b + 1]);
final int ac = comp.compare(x[2 * a], x[2 * a + 1], x[2 * c], x[2 * c + 1]);
final int bc = comp.compare(x[2 * b], x[2 * b + 1], x[2 * c], x[2 * c + 1]);
return ((ab < 0) ? ((bc < 0) ? b
: (ac < 0) ? c
: a)
: ((bc > 0) ? b
: (ac > 0) ? c
: a));
}
/**
* Merge sort 1.
*
* @param src the src
* @param dest the dest
* @param low the low
* @param high the high
*/
private static void mergeSort1(long src[], long dest[], int low, int high) {
final int length = high - low;
// Insertion sort on smallest arrays
if (length < SMALL) {
for (int i = low; i < high; i++) {
for (int j = i;
(j > low) &&
((dest[2 * j - 2] > dest[2 * j]) ||
((dest[2 * j - 2] == dest[2 * j]) && (dest[2 * j - 1] > dest[2 * j + 1])));
j--) {
swap(dest, j, j - 1);
}
}
return;
}
// Recursively sort halves of dest into src
final int mid = (low + high) / 2;
mergeSort1(dest, src, low, mid);
mergeSort1(dest, src, mid, high);
// If list is already sorted, just copy from src to dest. This is an
// optimization that results in faster sorts for nearly ordered lists.
if ((src[2 * mid - 2] < src[2 * mid]) ||
((src[2 * mid - 2] == src[2 * mid]) && (src[2 * mid - 1] <= src[2 * mid + 1]))) {
System.arraycopy(src, 2 * low, dest, 2 * low, length * 2 + 1);
return;
}
// Merge sorted halves (now in src) into dest
for (int i = low, p = low, q = mid; i < high; i++) {
if ((q >= high) ||
((p < mid) && (src[2 * p] < src[2 * q])) ||
((src[2 * p] == src[2 * q]) && (src[2 * p + 1] <= src[2 * q + 1]))) {
dest[2 * i] = src[p * 2];
dest[2 * i + 1] = src[p * 2 + 1];
p++;
} else {
dest[2 * i] = src[2 * q];
dest[2 * i + 1] = src[2 * q + 1];
q++;
}
}
}
/**
* Merge sort 1.
*
* @param src the src
* @param dest the dest
* @param low the low
* @param high the high
* @param c the c
*/
private static void mergeSort1(long src[], long dest[], int low, int high, UuidComparatorBI c) {
final int length = high - low;
// Insertion sort on smallest arrays
if (length < SMALL) {
for (int i = low; i < high; i++) {
for (int j = i;
(j > low) && (c.compare(dest[2 * j - 2], dest[2 * j - 1], dest[j * 2], dest[j * 2 + 1]) > 0); j--) {
swap(dest, j, j - 1);
}
}
return;
}
// Recursively sort halves of dest into src
final int mid = (low + high) / 2;
mergeSort1(dest, src, low, mid, c);
mergeSort1(dest, src, mid, high, c);
// If list is already sorted, just copy from src to dest. This is an
// optimization that results in faster sorts for nearly ordered lists.
if (c.compare(src[2 * mid - 2], src[2 * mid - 1], src[2 * mid], src[2 * mid + 1]) <= 0) {
System.arraycopy(src, low * 2, dest, low * 2, length * 2 + 1);
return;
}
// Merge sorted halves (now in src) into dest
for (int i = low, p = low, q = mid; i < high; i++) {
if ((q >= high) || ((p < mid) && (c.compare(src[2 * p], src[2 * p + 1], src[2 * q], src[2 * q + 1]) <= 0))) {
dest[2 * i] = src[2 * p];
dest[2 * i + 1] = src[2 * p + 1];
p++;
} else {
dest[2 * i] = src[2 * q];
dest[2 * i + 1] = src[2 * q + 1];
q++;
}
}
}
/**
* Sorts the specified sub-array of chars into ascending order.
*
* @param x the x
* @param off the off
* @param len the len
* @param comp the comp
*/
private static void quickSort1(long x[], int off, int len, UuidComparatorBI comp) {
// Insertion sort on smallest arrays
if (len < SMALL) {
for (int i = off; i < len + off; i++) {
for (int j = i; (j > off) && (comp.compare(x[2 * j - 2], x[2 * j - 1], x[2 * j], x[2 * j + 1]) > 0); j--) {
swap(x, j, j - 1);
}
}
return;
}
// Choose a partition element, v
int m = off + len / 2; // Small arrays, middle element
if (len > SMALL) {
int l = off;
int n = off + len - 1;
if (len > MEDIUM) { // Big arrays, pseudomedian of 9
final int s = len / 8;
l = med3(x, l, l + s, l + 2 * s, comp);
m = med3(x, m - s, m, m + s, comp);
n = med3(x, n - 2 * s, n - s, n, comp);
}
m = med3(x, l, m, n, comp); // Mid-size, med of 3
}
final long[] v = new long[2];
v[0] = x[m * 2];
v[1] = x[m * 2 + 1];
// Establish Invariant: v* (<v)* (>v)* v*
int a = off,
b = a,
c = off + len - 1,
d = c;
while (true) {
int comparison;
while ((b <= c) && (comparison = comp.compare(x[2 * b], x[2 * b + 1], v[0], v[1])) <= 0) {
if (comparison == 0) {
swap(x, a++, b);
}
b++;
}
while ((c >= b) && (comparison = comp.compare(x[2 * c], x[2 * c + 1], v[0], v[1])) >= 0) {
if (comparison == 0) {
swap(x, c, d--);
}
c--;
}
if (b > c) {
break;
}
swap(x, b++, c--);
}
// Swap partition elements back to middle
int s;
final int n = off + len;
s = Math.min(a - off, b - a);
vecswap(x, off, b - s, s);
s = Math.min(d - c, n - d - 1);
vecswap(x, b, n - s, s);
// Recursively sort non-partition-elements
if ((s = b - a) > 1) {
quickSort1(x, off, s, comp);
}
if ((s = d - c) > 1) {
quickSort1(x, n - s, s, comp);
}
}
/**
* Check that fromIndex and toIndex are in range, and throw an appropriate exception if they aren't.
*
* @param arrayLen the array len
* @param fromIndex the from index
* @param toIndex the to index
*/
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 * 2 - 1) {
throw new ArrayIndexOutOfBoundsException(toIndex);
}
}
/**
* Swaps x[a] with x[b].
*
* @param x the x
* @param a the a
* @param b the b
*/
private static void swap(long x[], int a, int b) {
final int aMsb = a * 2;
final int aLsb = aMsb + 1;
final int bMsb = b * 2;
final int bLsb = bMsb + 1;
final long[] t = new long[2];
t[0] = x[aMsb];
t[1] = x[aLsb];
x[aMsb] = x[bMsb];
x[aLsb] = x[bLsb];
x[bMsb] = t[0];
x[bLsb] = t[1];
}
/**
* Swaps x[a .. (a+n-1)] with x[b .. (b+n-1)].
*
* @param x the x
* @param a the a
* @param b the b
* @param n the n
*/
private static void vecswap(long x[], int a, int b, int n) {
for (int i = 0; i < n; i++, a++, b++) {
swap(x, a, b);
}
}
}