blob: 2f35e63cc50a42288825f73fc204d040b3eec110 [file] [log] [blame]
/*
* Copyright (c) 2014-2021 by Wen Yu
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License, v. 2.0 are satisfied: GNU General Public License, version 2
* or any later version.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
*
* Change History - most recent changes go on top of previous changes
*
* ArrayUtils.java
*
* Who Date Description
* ==== ========= ======================================================================
* WY 14Jun2015 Bug fix for toNBits() to use long data type internally
* WY 04Jun2015 Rewrote all concatenation related methods
* WY 02Jun2015 Bug fix for generic concatenate methods
* WY 06Apr2015 Added reverse(byte[]) to reverse byte array elements
* WY 06Jan2015 Added reverse() to reverse array elements
* WY 10Dec2014 Moved reverseBits() from IMGUtils to here along with BIT_REVERSE_TABLE
* WY 08Dec2014 Fixed bug for flipEndian() with more than 32 bit sample data
* WY 07Dec2014 Changed method names for byte array to other array types conversion
* WY 07Dec2014 Added new methods to work with floating point TIFF images
* WY 03Dec2014 Added byteArrayToFloatArray() and byteArrayToDoubleArray()
* WY 25Nov2014 Added removeDuplicates() to sort and remove duplicates from int arrays
* WY 12Nov2014 Changed the argument sequence for flipEndian()
* WY 11Nov2014 Changed flipEndian() to include scan line stride to skip bits
* WY 11Nov2014 Added toNBits() to convert byte array to nBits data unit
* WY 28Oct2014 Added flipEndian() to work with TIFTweaker mergeTiffImagesEx()
*/
package pixy.util;
import java.util.Arrays;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.lang.reflect.Array;
import java.nio.ByteOrder;
import java.util.AbstractList;
import java.nio.ByteBuffer;
import java.nio.DoubleBuffer;
import java.nio.FloatBuffer;
import java.nio.IntBuffer;
import java.nio.LongBuffer;
import java.nio.ShortBuffer;
/**
* Array utility class
*
* @author Wen Yu, yuwen_66@yahoo.com
* @version 1.0 09/18/2012
*/
public class ArrayUtils
{
// Bit mask 0 - 32 bits (inclusive)
private static final int[] MASK = { 0x000,
0x1, 0x3, 0x7, 0xf,
0x1f, 0x3f, 0x7f, 0xff,
0x1ff, 0x3ff, 0x7ff, 0xfff,
0x1fff, 0x3fff, 0x7fff, 0xffff,
0x1ffff, 0x3ffff, 0x7ffff, 0xfffff,
0x1fffff, 0x3fffff, 0x7fffff, 0xffffff,
0x1ffffff, 0x3ffffff, 0x7ffffff, 0xfffffff,
0x1fffffff, 0x3fffffff, 0x7fffffff, 0xffffffff // 32 bits
};
// Bit reverse table to work with TIFF FillOrder field.
private static final byte[] BIT_REVERSE_TABLE = {
(byte)0x00, (byte)0x80, (byte)0x40, (byte)0xc0, (byte)0x20, (byte)0xa0, (byte)0x60, (byte)0xe0,
(byte)0x10, (byte)0x90, (byte)0x50, (byte)0xd0, (byte)0x30, (byte)0xb0, (byte)0x70, (byte)0xf0,
(byte)0x08, (byte)0x88, (byte)0x48, (byte)0xc8, (byte)0x28, (byte)0xa8, (byte)0x68, (byte)0xe8,
(byte)0x18, (byte)0x98, (byte)0x58, (byte)0xd8, (byte)0x38, (byte)0xb8, (byte)0x78, (byte)0xf8,
(byte)0x04, (byte)0x84, (byte)0x44, (byte)0xc4, (byte)0x24, (byte)0xa4, (byte)0x64, (byte)0xe4,
(byte)0x14, (byte)0x94, (byte)0x54, (byte)0xd4, (byte)0x34, (byte)0xb4, (byte)0x74, (byte)0xf4,
(byte)0x0c, (byte)0x8c, (byte)0x4c, (byte)0xcc, (byte)0x2c, (byte)0xac, (byte)0x6c, (byte)0xec,
(byte)0x1c, (byte)0x9c, (byte)0x5c, (byte)0xdc, (byte)0x3c, (byte)0xbc, (byte)0x7c, (byte)0xfc,
(byte)0x02, (byte)0x82, (byte)0x42, (byte)0xc2, (byte)0x22, (byte)0xa2, (byte)0x62, (byte)0xe2,
(byte)0x12, (byte)0x92, (byte)0x52, (byte)0xd2, (byte)0x32, (byte)0xb2, (byte)0x72, (byte)0xf2,
(byte)0x0a, (byte)0x8a, (byte)0x4a, (byte)0xca, (byte)0x2a, (byte)0xaa, (byte)0x6a, (byte)0xea,
(byte)0x1a, (byte)0x9a, (byte)0x5a, (byte)0xda, (byte)0x3a, (byte)0xba, (byte)0x7a, (byte)0xfa,
(byte)0x06, (byte)0x86, (byte)0x46, (byte)0xc6, (byte)0x26, (byte)0xa6, (byte)0x66, (byte)0xe6,
(byte)0x16, (byte)0x96, (byte)0x56, (byte)0xd6, (byte)0x36, (byte)0xb6, (byte)0x76, (byte)0xf6,
(byte)0x0e, (byte)0x8e, (byte)0x4e, (byte)0xce, (byte)0x2e, (byte)0xae, (byte)0x6e, (byte)0xee,
(byte)0x1e, (byte)0x9e, (byte)0x5e, (byte)0xde, (byte)0x3e, (byte)0xbe, (byte)0x7e, (byte)0xfe,
(byte)0x01, (byte)0x81, (byte)0x41, (byte)0xc1, (byte)0x21, (byte)0xa1, (byte)0x61, (byte)0xe1,
(byte)0x11, (byte)0x91, (byte)0x51, (byte)0xd1, (byte)0x31, (byte)0xb1, (byte)0x71, (byte)0xf1,
(byte)0x09, (byte)0x89, (byte)0x49, (byte)0xc9, (byte)0x29, (byte)0xa9, (byte)0x69, (byte)0xe9,
(byte)0x19, (byte)0x99, (byte)0x59, (byte)0xd9, (byte)0x39, (byte)0xb9, (byte)0x79, (byte)0xf9,
(byte)0x05, (byte)0x85, (byte)0x45, (byte)0xc5, (byte)0x25, (byte)0xa5, (byte)0x65, (byte)0xe5,
(byte)0x15, (byte)0x95, (byte)0x55, (byte)0xd5, (byte)0x35, (byte)0xb5, (byte)0x75, (byte)0xf5,
(byte)0x0d, (byte)0x8d, (byte)0x4d, (byte)0xcd, (byte)0x2d, (byte)0xad, (byte)0x6d, (byte)0xed,
(byte)0x1d, (byte)0x9d, (byte)0x5d, (byte)0xdd, (byte)0x3d, (byte)0xbd, (byte)0x7d, (byte)0xfd,
(byte)0x03, (byte)0x83, (byte)0x43, (byte)0xc3, (byte)0x23, (byte)0xa3, (byte)0x63, (byte)0xe3,
(byte)0x13, (byte)0x93, (byte)0x53, (byte)0xd3, (byte)0x33, (byte)0xb3, (byte)0x73, (byte)0xf3,
(byte)0x0b, (byte)0x8b, (byte)0x4b, (byte)0xcb, (byte)0x2b, (byte)0xab, (byte)0x6b, (byte)0xeb,
(byte)0x1b, (byte)0x9b, (byte)0x5b, (byte)0xdb, (byte)0x3b, (byte)0xbb, (byte)0x7b, (byte)0xfb,
(byte)0x07, (byte)0x87, (byte)0x47, (byte)0xc7, (byte)0x27, (byte)0xa7, (byte)0x67, (byte)0xe7,
(byte)0x17, (byte)0x97, (byte)0x57, (byte)0xd7, (byte)0x37, (byte)0xb7, (byte)0x77, (byte)0xf7,
(byte)0x0f, (byte)0x8f, (byte)0x4f, (byte)0xcf, (byte)0x2f, (byte)0xaf, (byte)0x6f, (byte)0xef,
(byte)0x1f, (byte)0x9f, (byte)0x5f, (byte)0xdf, (byte)0x3f, (byte)0xbf, (byte)0x7f, (byte)0xff
};
// From Effective Java 2nd Edition.
public static List<Integer> asList(final int[] a)
{
if (a == null)
throw new NullPointerException();
return new AbstractList<Integer>() {// Concrete implementation built atop skeletal implementation
public Integer get(int i) {
return a[i];
}
@Override public Integer set(int i, Integer val) {
int oldVal = a[i];
a[i] = val;
return oldVal;
}
public int size() {
return a.length;
}
};
}
public static void bubbleSort(int[] array) {
int n = array.length;
boolean doMore = true;
while (doMore) {
n--;
doMore = false; // assume this is our last pass over the array
for (int i=0; i < n; i++) {
if (array[i] > array[i+1]) {
// exchange elements
int temp = array[i];
array[i] = array[i+1];
array[i+1] = temp;
doMore = true; // after an exchange, must look again
}
}
}
}
public static <T extends Comparable<? super T>> void bubbleSort(T[] array) {
int n = array.length;
boolean doMore = true;
while (doMore) {
n--;
doMore = false; // assume this is our last pass over the array
for (int i = 0; i < n; i++) {
if (array[i].compareTo(array[i+1]) > 0) {
// exchange elements
T temp = array[i];
array[i] = array[i+1];
array[i+1] = temp;
doMore = true; // after an exchange, must look again
}
}
}
}
/**
* Since Set doesn't allow duplicates add() return false
* if we try to add duplicates into Set and this property
* can be used to check if array contains duplicates.
*
* @param input input array
* @return true if input array contains duplicates, otherwise false.
*/
public static <T> boolean checkDuplicate(T[] input) {
Set<T> tempSet = new HashSet<T>();
for (T str : input) {
if (!tempSet.add(str)) {
return true;
}
}
return false;
}
public static byte[] concat(byte[] first, byte[]... rest) {
if(first == null) {
throw new IllegalArgumentException("Firt element is null");
}
if(rest.length == 0) return first;
// Now the real stuff
int totalLength = first.length;
for (byte[] array : rest) {
totalLength += array.length;
}
byte[] result = new byte[totalLength];
int offset = first.length;
System.arraycopy(first, 0, result, 0, offset);
for (byte[] array : rest) {
System.arraycopy(array, 0, result, offset, array.length);
offset += array.length;
}
return result;
}
/**
* Type safe concatenation of arrays with upper type bound T
* <p>
* Note: if type parameter is not explicitly supplied, it will be inferred as the
* upper bound for the two parameters.
*
* @param arrays the arrays to be concatenated
* @return a concatenation of the input arrays
* @throws NullPointerException if any of the input array is null
*/
public static <T> T[] concat(T[]... arrays) {
if(arrays.length == 0)
throw new IllegalArgumentException("Varargs length is zero");
if(arrays.length == 1) return arrays[0];
// Now the real stuff
int totalLength = 0;
// Taking advantage of the compiler type inference
Class<?> returnType = arrays.getClass().getComponentType().getComponentType();
for (T[] array : arrays)
totalLength += array.length;
@SuppressWarnings("unchecked")
T[] result = (T[]) Array.newInstance(returnType, totalLength);
int offset = 0;
for (T[] array : arrays) {
System.arraycopy(array, 0, result, offset, array.length);
offset += array.length;
}
return result;
}
/**
* Type safe concatenation of arrays with upper type bound T
*
* @param type type bound for the concatenated array
* @param arrays arrays to be concatenated
* @return a concatenation of the arrays.
* @throws NullPointerException if any of the arrays to be
* concatenated is null.
*/
public static <T> T[] concat(Class<T> type, T[]... arrays) {
if(type == null)
throw new IllegalArgumentException("Input type class is null");
if(arrays.length == 0) { // Return a zero length array instead of null
@SuppressWarnings("unchecked")
T[] result = (T[]) Array.newInstance(type, 0);
return result;
}
// Make sure we have at least two arrays to concatenate
if(arrays.length == 1) return arrays[0];
int totalLength = 0;
for (T[] array : arrays)
totalLength += array.length;
@SuppressWarnings("unchecked")
T[] result = (T[]) Array.newInstance(type, totalLength);
int offset = 0;
for (T[] array : arrays) {
System.arraycopy(array, 0, result, offset, array.length);
offset += array.length;
}
return result;
}
public static int findEqualOrLess(int[] a, int key) {
return findEqualOrLess(a, 0, a.length, key);
}
/**
* Find the index of the element which is equal or less than the key.
* The array must be sorted in ascending order.
*
* @param a the array to be searched
* @param key the value to be searched for
* @return index of the search key or index of the element which is closest to but less than the search key or
* -1 is the search key is less than the first element of the array.
*/
public static int findEqualOrLess(int[] a, int fromIndex, int toIndex, int key) {
int index = Arrays.binarySearch(a, fromIndex, toIndex, key);
// -index - 1 is the insertion point if not found, so -index - 1 -1 is the less position
if(index < 0) {
index = -index - 1 - 1;
}
// The index of the element which is either equal or less than the key
return index;
}
public static <T> int findEqualOrLess(T[] a, int fromIndex, int toIndex, T key, Comparator<? super T> c) {
int index = Arrays.binarySearch(a, fromIndex, toIndex, key, c);
// -index - 1 is the insertion point if not found
if(index < 0) {
index = -index - 1 - 1;
}
return index;
}
public static <T> int findEqualOrLess(T[] a, T key, Comparator<? super T> c) {
return findEqualOrLess(a, 0, a.length, key, c);
}
/**
* Flip the endian of the input byte-compacted array
*
* @param input the input byte array which is byte compacted
* @param offset the offset to start the reading input
* @param len number of bytes to read
* @param bits number of bits for the data before compaction
* @param scanLineStride scan line stride to skip bits
* @param bigEndian whether or not the input data is in big endian order
*
* @return a byte array with the endian flipped
*/
public static byte[] flipEndian(byte[] input, int offset, int len, int bits, int scanLineStride, boolean bigEndian) {
long value = 0;
int bits_remain = 0;
long temp_byte = 0; // Must make this long, otherwise will give wrong result for bits > 32
int empty_bits = 8;
byte[] output = new byte[input.length];
int strideCounter = 0;
int end = offset + len;
int bufIndex = 0;
int temp = bits;
boolean bigEndianOut = !bigEndian;
loop:
while(true) {
if(!bigEndian)
value = (temp_byte >> (8-bits_remain));
else
value = (temp_byte & MASK[bits_remain]);
while (bits > bits_remain)
{
if(offset >= end) {
break loop;
}
temp_byte = input[offset++]&0xff;
if(bigEndian)
value = ((value<<8)|temp_byte);
else
value |= (temp_byte<<bits_remain);
bits_remain += 8;
}
bits_remain -= bits;
if(bigEndian)
value = (value>>bits_remain);
// Write bits bit length value in opposite endian
if(bigEndianOut) {
temp = bits-empty_bits;
output[bufIndex] |= ((value>>temp)&MASK[empty_bits]);
while(temp > 8)
{
output[++bufIndex] |= ((value>>(temp-8))&MASK[8]);
temp -= 8;
}
if(temp > 0) {
output[++bufIndex] |= ((value&MASK[temp])<<(8-temp));
temp -= 8;
}
} else { // Little endian
temp = bits;
output[bufIndex] |= ((value&MASK[empty_bits])<<(8-empty_bits));
value >>= empty_bits;
temp -= empty_bits;
// If the code is longer than the empty_bits
while(temp > 8) {
output[++bufIndex] |= (value&0xff);
value >>= 8;
temp -= 8;
}
if(temp > 0)
{
output[++bufIndex] |= (value&MASK[temp]);
temp -= 8;
}
}
empty_bits = -temp;
if(++strideCounter%scanLineStride == 0) {
empty_bits = 0;
bits_remain = 0;
}
}
return output;
}
// From http://stackoverflow.com/questions/6162651/half-precision-floating-point-in-java
// returns all higher 16 bits as 0 for all results
public static int fromFloat(float fval)
{
int fbits = Float.floatToIntBits(fval);
int sign = fbits >>> 16 & 0x8000; // sign only
int val = (fbits & 0x7fffffff) + 0x1000; // rounded value
if(val >= 0x47800000) // might be or become NaN/Inf
{ // avoid Inf due to rounding
if( (fbits & 0x7fffffff) >= 0x47800000)
{ // is or must become NaN/Inf
if(val < 0x7f800000) // was value but too large
return sign | 0x7c00; // make it +/-Inf
return sign | 0x7c00 | // remains +/-Inf or NaN
(fbits & 0x007fffff) >>> 13; // keep NaN (and Inf) bits
}
return sign | 0x7bff; // unrounded not quite Inf
}
if(val >= 0x38800000) // remains normalized value
return sign | val - 0x38000000 >>> 13; // exp - 127 + 15
if(val < 0x33000000) // too small for subnormal
return sign; // becomes +/-0
val = (fbits & 0x7fffffff) >>> 23; // tmp exp for subnormal calc
return sign | ((fbits & 0x7fffff | 0x800000) // add subnormal bit
+ (0x800000 >>> val - 102) // round depending on cut off
>>> 126 - val); // div by 2^(1-(exp-127+15)) and >> 13 | exp=0
}
/**
* Since nonzero-length array is always mutable, we should return
* a clone of the underlying array as BIT_REVERSE_TABLE.clone().
*
* @return the byte reverse table.
*/
public static byte[] getBitReverseTable() {
return BIT_REVERSE_TABLE.clone();
}
// Insertion sort
public static void insertionsort(int[] array) {
insertionsort(array, 0, array.length - 1);
}
public static void insertionsort(int[] array, int start, int end) {
int j;
for (int i = start + 1; i < end + 1; i++)
{
int temp = array[i];
for ( j = i; j > start && temp <= array[j-1]; j-- )
array[j] = array[j-1];
// Move temp to the right place
array[j] = temp;
}
}
// Insertion sort
public static <T extends Comparable<? super T>> void insertionsort(T[] array) {
insertionsort(array, 0, array.length - 1);
}
// Insertion sort
public static <T extends Comparable<? super T>> void insertionsort(T[] array, int start, int end) {
int j;
for (int i = start + 1; i < end + 1; i++)
{
T temp = array[i];
for ( j = i; j > start && temp.compareTo(array[j-1]) <= 0; j-- )
array[j] = array[j-1];
// Move temp to the right place
array[j] = temp;
}
}
// Merge sort
public static void mergesort(int[] array) {
mergesort(array, new int[array.length], 0, array.length - 1);
}
public static void mergesort(int[] array, int left, int right) {
if(left < 0 || right > array.length - 1) throw new IllegalArgumentException("Array index out of bounds");
mergesort(array, new int[array.length], left, right);
}
private static void mergesort(int[] array, int[] temp, int left, int right) {
// check the base case
if (left < right) {
// Get the index of the element which is in the middle
int middle = left + (right - left) / 2;
// Sort the left side of the array
mergesort(array, temp, left, middle);
// Sort the right side of the array
mergesort(array, temp, middle + 1, right);
// Merge the left and the right
merge(array, temp, left, middle, right);
}
}
public static <T extends Comparable<? super T>> void mergesort(T[] array) {
mergesort(array, 0, array.length - 1);
}
public static <T extends Comparable<? super T>> void mergesort(T[] array, int left, int right) {
if(left < 0 || right > array.length - 1) throw new IllegalArgumentException("Array index out of bounds");
@SuppressWarnings("unchecked")
T[] temp = (T[]) Array.newInstance(array.getClass().getComponentType(), array.length);
mergesort(array, temp, left, right);
}
// Merge sort
private static <T extends Comparable<? super T>> void mergesort(T[] array, T[] temp, int left, int right) {
// check the base case
if (left < right) {
// Get the index of the element which is in the middle
int middle = left + (right - left) / 2;
// Sort the left side of the array
mergesort(array, temp, left, middle);
// Sort the right side of the array
mergesort(array, temp, middle + 1, right);
// Merge the left and the right
merge(array, temp, left, middle, right);
}
}
private static <T extends Comparable<? super T>> void merge(T[] array, T[] temp, int left, int middle, int right) {
// Copy both parts into the temporary array
for (int i = left; i <= right; i++) {
temp[i] = array[i];
}
int i = left;
int j = middle + 1;
int k = left;
while (i <= middle && j <= right) {
if (temp[i].compareTo(temp[j]) <= 0) {
array[k] = temp[i];
i++;
} else {
array[k] = temp[j];
j++;
}
k++;
}
while (i <= middle) {
array[k] = temp[i];
k++;
i++;
}
}
private static void merge(int[] array, int[] temp, int left, int middle, int right) {
// Copy both parts into the temporary array
for (int i = left; i <= right; i++) {
temp[i] = array[i];
}
int i = left;
int j = middle + 1;
int k = left;
while (i <= middle && j <= right) {
if (temp[i] <= temp[j]) {
array[k] = temp[i];
i++;
} else {
array[k] = temp[j];
j++;
}
k++;
}
while (i <= middle) {
array[k] = temp[i];
k++;
i++;
}
}
/**
* Packs all or part of the input byte array which uses "bits" bits to use all 8 bits.
*
* @param input input byte array
* @param start offset of the input array to start packing
* @param bits number of bits used by the input array
* @param len number of bytes from the input to be packed
* @return the packed byte array
*/
public static byte[] packByteArray(byte[] input, int start, int bits, int len) {
//
if(bits == 8) return ArrayUtils.subArray(input, start, len);
if(bits > 8 || bits <= 0) throw new IllegalArgumentException("Invalid value of bits: " + bits);
byte[] packedBytes = new byte[(bits*len + 7)>>3];
short mask[] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
int index = 0;
int empty_bits = 8;
int end = start + len;
for(int i = start; i < end; i++) {
// If we have enough space for input byte, one step operation
if(empty_bits >= bits) {
packedBytes[index] |= ((input[i]&mask[bits])<<(empty_bits-bits));
empty_bits -= bits;
if(empty_bits == 0) {
index++;
empty_bits = 8;
}
} else { // Otherwise two step operation
packedBytes[index++] |= ((input[i]>>(bits-empty_bits))&mask[empty_bits]);
packedBytes[index] |= ((input[i]&mask[bits-empty_bits])<<(8-bits+empty_bits));
empty_bits += (8-bits);
}
}
return packedBytes;
}
/**
* Packs all or part of the input byte array which uses "bits" bits to use all 8 bits.
* <p>
* We assume len is a multiplication of stride. The parameter stride controls the packing
* unit length and different units <b>DO NOT</b> share same byte. This happens when packing
* image data where each scan line <b>MUST</b> start at byte boundary like TIFF.
*
* @param input input byte array to be packed
* @param stride length of packing unit
* @param start offset of the input array to start packing
* @param bits number of bits used in each byte of the input
* @param len number of input bytes to be packed
* @return the packed byte array
*/
public static byte[] packByteArray(byte[] input, int stride, int start, int bits, int len) {
//
if(bits == 8) return ArrayUtils.subArray(input, start, len);
if(bits > 8 || bits <= 0) throw new IllegalArgumentException("Invalid value of bits: " + bits);
int bitsPerStride = bits*stride;
int numOfStrides = len/stride;
byte[] packedBytes = new byte[((bitsPerStride + 7)>>3)*numOfStrides];
short mask[] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
int index = 0;
int empty_bits = 8;
int end = start + len;
int strideCounter = 0;
for(int i = start; i < end; i++) {
// If we have enough space for input byte, one step operation
if(empty_bits >= bits) {
packedBytes[index] |= ((input[i]&mask[bits])<<(empty_bits-bits));
empty_bits -= bits;
} else { // Otherwise, split the pixel between two bytes.
//This will never happen for 1, 2, 4, 8 bits color depth image
packedBytes[index++] |= ((input[i]>>(bits-empty_bits))&mask[empty_bits]);
packedBytes[index] |= ((input[i]&mask[bits-empty_bits])<<(8-bits+empty_bits));
empty_bits += (8-bits);
}
// Check to see if we need to move to next byte
if(++strideCounter%stride == 0 || empty_bits == 0) {
index++;
empty_bits = 8;
}
}
return packedBytes;
}
// Quick sort
public static void quicksort(int[] array) {
quicksort (array, 0, array.length - 1);
}
public static void quicksort (int[] array, int start, int end) {
int inner = start;
int outer = end;
int mid = (start + end) / 2;
do {
// work in from the start until we find a swap to the
// other partition is needed
while ((inner < mid) && (array[inner] <= array[mid]))
inner++;
// work in from the end until we find a swap to the
// other partition is needed
while ((outer > mid) && (array[outer] >= array[mid]))
outer--;
// if inner index <= outer index, swap elements
if (inner < mid && outer > mid) {
swap(array, inner, outer);
inner++;
outer--;
} else if (inner < mid) {
swap(array, inner, mid - 1);
swap(array, mid, mid - 1);
mid--;
} else if (outer >mid) {
swap(array, outer, mid + 1);
swap(array, mid, mid + 1);
mid++;
}
} while (inner !=outer);
// recursion
if ((mid - 1) > start) quicksort(array, start, mid - 1);
if (end > (mid + 1)) quicksort(array, mid + 1, end);
}
// Quick sort
public static <T extends Comparable<? super T>> void quicksort (T[] array) {
quicksort(array, 0, array.length - 1);
}
// Quick sort
public static <T extends Comparable<? super T>> void quicksort (T[] array, int low, int high) {
int i = low, j = high;
// Get the pivot element from the middle of the list
T pivot = array[low + (high-low)/2];
// Divide into two lists
while (i <= j) {
// If the current value from the left list is smaller then the pivot
// element then get the next element from the left list
while (array[i].compareTo(pivot) < 0) {
i++;
}
// If the current value from the right list is larger then the pivot
// element then get the next element from the right list
while (array[j].compareTo(pivot) > 0) {
j--;
}
// If we have found a values in the left list which is larger then
// the pivot element and if we have found a value in the right list
// which is smaller then the pivot element then we exchange the
// values.
// As we are done we can increase i and j
if (i <= j) {
swap(array, i, j);
i++;
j--;
}
}
// Recursion
if (low < j)
quicksort(array, low, j);
if (i < high)
quicksort(array, i, high);
}
// Based on java2novice.com example
/**
* Remove duplicate elements from an int array
*
* @param input input unsorted int array
* @return a sorted int array with unique elements
*/
public static int[] removeDuplicates(int[] input) {
//return if the array length is less than 2
if(input.length < 2){
return input;
}
// Sort the array first
Arrays.sort(input);
int j = 0;
int i = 1;
while(i < input.length){
if(input[i] == input[j]){
i++;
} else{
input[++j] = input[i++];
}
}
int[] output = new int[j + 1];
System.arraycopy(input, 0, output, 0, j + 1);
return output;
}
// Reverse the bit order (bit sex) of a byte array
public static void reverseBits(byte[] input) {
for(int i = input.length - 1; i >= 0; i--)
input[i] = BIT_REVERSE_TABLE[input[i]&0xff];
}
public static byte[] reverse(byte[] array) {
if (array == null)
throw new IllegalArgumentException("Input array is null");
int left = 0;
int right = array.length - 1;
byte tmp;
while (left < right) {
tmp = array[right];
array[right] = array[left];
array[left] = tmp;
left++;
right--;
}
return array;
}
// Reverse the array
public static <T> void reverse(T[] data) {
for (int left = 0, right = data.length - 1; left < right; left++, right--) {
T temp = data[left];
data[left] = data[right];
data[right] = temp;
}
}
// Shell sort
public static void shellsort(int[] array) {
shellsort(array, 0, array.length - 1);
}
public static void shellsort(int[] array, int start, int end) {
if(start < 0 || end < 0 || start > end || end > array.length -1) throw new IllegalArgumentException("Array index out of bounds");
int gap = 1;
int len = end - start + 1;
// Generate Knuth sequence 1, 4, 13, 40, 121, 364,1093, 3280, 9841 ...
while(gap < len) gap = 3*gap + 1;
while ( gap > 0 )
{
int begin = start + gap;
for (int i = begin; i <= end; i++)
{
int temp = array[i];
int j = i;
while ( j >= begin && temp <= array[j - gap])
{
array[j] = array[j - gap];
j -= gap;
}
array[j] = temp;
}
gap /= 3;
}
}
// Shell sort
public static <T extends Comparable<? super T>> void shellsort(T[] array) {
shellsort(array, 0, array.length - 1);
}
// Shell sort
public static <T extends Comparable<? super T>> void shellsort(T[] array, int start, int end) {
if(start < 0 || end < 0 || start > end || end > array.length - 1) throw new IllegalArgumentException("Array index out of bounds");
int gap = 1;
int len = end - start + 1;
// Generate Knuth sequence 1, 4, 13, 40, 121, 364,1093, 3280, 9841 ...
while(gap < len) gap = 3*gap + 1;
while ( gap > 0 )
{
int begin = start + gap;
for (int i = begin; i <= end; i++)
{
T temp = array[i];
int j = i;
while ( j >= begin && temp.compareTo(array[j - gap]) <= 0)
{
array[j] = array[j - gap];
j -= gap;
}
array[j] = temp;
}
gap /= 3;
}
}
public static byte[] subArray(byte[] src, int offset, int len) {
if(offset == 0 && len == src.length) return src;
if((offset < 0 || offset >= src.length) || (offset + len > src.length))
throw new IllegalArgumentException("Copy range out of array bounds");
byte[] dest = new byte[len];
System.arraycopy(src, offset, dest, 0, len);
return dest;
}
private static final void swap(int[] array, int a, int b) {
int temp = array[a];
array[a] = array[b];
array[b] = temp;
}
private static final <T> void swap(T[] array, int a, int b) {
T temp = array[a];
array[a] = array[b];
array[b] = temp;
}
public static float[] to16BitFloatArray(byte[] data, boolean bigEndian) {
short[] shorts = (short[])toNBits(16, data, Integer.MAX_VALUE, bigEndian);
float[] floats = new float[shorts.length];
for(int i = 0; i < floats.length; i++) {
floats[i] = toFloat(shorts[i]);
}
return floats;
}
/*
* Tries to convert 24 bit floating point sample to 32 bit float data.
* Up to now, there has been no way to do it correctly and there might be no
* correct way to do this because 24 bit is not an IEEE floating point type.
* 24 bit floating point images appear too dark using this conversion.
*/
public static float[] to24BitFloatArray(byte[] data, boolean bigEndian) {
int[] ints = (int[])toNBits(24, data, Integer.MAX_VALUE, bigEndian);
float[] floats = new float[ints.length];
for(int i = 0; i < floats.length; i++) {
/**
int bits = ints[i]<<8;
int sign = ((bits & 0x80000000) == 0) ? 1 : -1;
int exponent = ((bits & 0x7f800000) >> 23);
int mantissa = (bits & 0x007fffff);
mantissa |= 0x00800000;
floats[i] = (float)(sign * mantissa * Math.pow(2, exponent-150));
*/
floats[i] = Float.intBitsToFloat(ints[i]<<8);
}
return floats;
}
// Convert byte array to long array, then to integer array discarding the higher bits
public static int[] to32BitsLongArray(byte[] data, boolean bigEndian) {
ByteBuffer byteBuffer = ByteBuffer.wrap(data);
if (bigEndian) {
byteBuffer.order(ByteOrder.BIG_ENDIAN);
} else {
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
LongBuffer longBuf = byteBuffer.asLongBuffer();
long[] array = new long[longBuf.remaining()];
longBuf.get(array);
int[] iArray = new int[array.length];
int i = 0;
for(long l : array) {
iArray[i++] = (int)l;
}
return iArray;
}
public static byte[] toByteArray(int value) {
return new byte[] {
(byte)value,
(byte)(value >>> 8),
(byte)(value >>> 16),
(byte)(value >>> 24)
};
}
public static byte[] toByteArray(int[] data, boolean bigEndian) {
ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 4);
if (bigEndian) {
byteBuffer.order(ByteOrder.BIG_ENDIAN);
} else {
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
IntBuffer intBuffer = byteBuffer.asIntBuffer();
intBuffer.put(data);
byte[] array = byteBuffer.array();
return array;
}
public static byte[] toByteArray(long[] data, boolean bigEndian) {
ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 8);
if (bigEndian) {
byteBuffer.order(ByteOrder.BIG_ENDIAN);
} else {
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
LongBuffer longBuffer = byteBuffer.asLongBuffer();
longBuffer.put(data);
byte[] array = byteBuffer.array();
return array;
}
public static byte[] toByteArray(short value) {
return new byte[] {
(byte)value, (byte)(value >>> 8)};
}
public static byte[] toByteArray(short[] data, boolean bigEndian) {
ByteBuffer byteBuffer = ByteBuffer.allocate(data.length * 2);
if (bigEndian) {
byteBuffer.order(ByteOrder.BIG_ENDIAN);
} else {
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
ShortBuffer shortBuffer = byteBuffer.asShortBuffer();
shortBuffer.put(data);
byte[] array = byteBuffer.array();
return array;
}
public static byte[] toByteArrayMM(int value) {
return new byte[] {
(byte)(value >>> 24),
(byte)(value >>> 16),
(byte)(value >>> 8),
(byte)value};
}
public static byte[] toByteArrayMM(short value) {
return new byte[] {
(byte)(value >>> 8), (byte)value};
}
public static double[] toDoubleArray(byte[] data, boolean bigEndian) {
return toDoubleArray(data, 0, data.length, bigEndian);
}
public static double[] toDoubleArray(byte[] data, int offset, int len, boolean bigEndian) {
ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, len);
if (bigEndian) {
byteBuffer.order(ByteOrder.BIG_ENDIAN);
} else {
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
DoubleBuffer doubleBuf = byteBuffer.asDoubleBuffer();
double[] array = new double[doubleBuf.remaining()];
doubleBuf.get(array);
return array;
}
// From http://stackoverflow.com/questions/6162651/half-precision-floating-point-in-java
// Converts integer to float ignores the higher 16 bits
public static float toFloat(int lbits) {
// ignores the higher 16 bits
int mant = lbits & 0x03ff; // 10 bits mantissa
int exp = lbits & 0x7c00; // 5 bits exponent
if(exp == 0x7c00 ) { // NaN/Inf
exp = 0x3fc00; // -> NaN/Inf
} else if(exp != 0) { // normalized value
exp += 0x1c000; // exp - 15 + 127
if( mant == 0 && exp > 0x1c400) // smooth transition
return Float.intBitsToFloat(
( lbits & 0x8000) << 16
| exp << 13 | 0x3ff);
} else if(mant != 0) { // && exp==0 -> subnormal
exp = 0x1c400; // make it normal
do {
mant <<= 1; // mantissa * 2
exp -= 0x400; // decrease exp by 1
} while((mant & 0x400) == 0); // while not normal
mant &= 0x3ff; // discard subnormal bit
} // else +/-0 -> +/-0
return Float.intBitsToFloat( // combine all parts
( lbits & 0x8000 ) << 16 // sign << ( 31 - 15 )
| ( exp | mant ) << 13 ); // value << ( 23 - 10 )
}
public static float[] toFloatArray(byte[] data, boolean bigEndian) {
return toFloatArray(data, 0, data.length, bigEndian);
}
public static float[] toFloatArray(byte[] data, int offset, int len, boolean bigEndian) {
ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, len);
if (bigEndian) {
byteBuffer.order(ByteOrder.BIG_ENDIAN);
} else {
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
FloatBuffer floatBuf = byteBuffer.asFloatBuffer();
float[] array = new float[floatBuf.remaining()];
floatBuf.get(array);
return array;
}
public static int[] toIntArray(byte[] data, boolean bigEndian) {
return toIntArray(data, 0, data.length, bigEndian);
}
public static int[] toIntArray(byte[] data, int offset, int len, boolean bigEndian) {
ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, len);
if (bigEndian) {
byteBuffer.order(ByteOrder.BIG_ENDIAN);
} else {
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
IntBuffer intBuf = byteBuffer.asIntBuffer();
int[] array = new int[intBuf.remaining()];
intBuf.get(array);
return array;
}
public static long[] toLongArray(byte[] data, boolean bigEndian) {
return toLongArray(data, 0, data.length, bigEndian);
}
public static long[] toLongArray(byte[] data, int offset, int len, boolean bigEndian) {
ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, len);
if (bigEndian) {
byteBuffer.order(ByteOrder.BIG_ENDIAN);
} else {
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
LongBuffer longBuf = byteBuffer.asLongBuffer();
long[] array = new long[longBuf.remaining()];
longBuf.get(array);
return array;
}
/**
* Converts an input byte array to nBits data array using the smallest data type which
* can hold the nBits data. Each data type contains only one data element.
*
* @param nBits number of bits for the data element
* @param input the input array for the data elements
* @param stride scan line stride used to discard remaining bits
* @param bigEndian the packing order of the bits. True if bigEndian, otherwise false.
*
* @return an array of the smallest data type which can hold the nBits data
*/
public static Object toNBits(int nBits, byte[] input, int stride, boolean bigEndian) {
int value = 0;
int bits_remain = 0;
int temp_byte = 0;
byte[] byteOutput = null;
short[] shortOutput = null;
int[] intOutput = null;
Object output = null;
int outLen = (int)((input.length*8L + nBits - 1)/nBits);
if(nBits <= 8) {
byteOutput = new byte[outLen];
output = byteOutput;
} else if(nBits <= 16) {
shortOutput = new short[outLen];
output = shortOutput;
} else if(nBits <= 32){
intOutput = new int[outLen];
output = intOutput;
} else {
throw new IllegalArgumentException("nBits exceeds limit - maximum 32");
}
int offset = 0;
int index = 0;
int strideCounter = 0;
loop:
while(true) {
if(!bigEndian)
value = (temp_byte >> (8-bits_remain));
else
value = (temp_byte & MASK[bits_remain]);
while (nBits > bits_remain)
{
if(offset >= input.length) {
break loop;
}
temp_byte = input[offset++]&0xff;
if(bigEndian)
value = ((value<<8)|temp_byte);
else
value |= (temp_byte<<bits_remain);
bits_remain += 8;
}
bits_remain -= nBits;
if(bigEndian)
value = (value>>(bits_remain));
value &= MASK[nBits];
if(++strideCounter%stride == 0) {
bits_remain = 0; // Discard the remaining bits
}
if(nBits <= 8) byteOutput[index++] = (byte)value;
else if(nBits <= 16) shortOutput[index++] = (short)value;
else intOutput[index++] = value;
}
return output;
}
public static double[] toPrimitive(Double[] doubles) {
double[] dArray = new double[doubles.length];
int i = 0;
for (double d : doubles) {
dArray[i++] = d;
}
return dArray;
}
public static float[] toPrimitive(Float[] floats) {
float[] fArray = new float[floats.length];
int i = 0;
for (float f : floats) {
fArray[i++] = f;
}
return fArray;
}
public static int[] toPrimitive(Integer[] integers) {
int[] ints = new int[integers.length];
int i = 0;
for (int n : integers) {
ints[i++] = n;
}
return ints;
}
public static long[] toPrimitive(Long[] longs) {
long[] lArray = new long[longs.length];
int i = 0;
for (long l : longs) {
lArray[i++] = l;
}
return lArray;
}
public static short[] toPrimitive(Short[] shorts) {
short[] sArray = new short[shorts.length];
int i = 0;
for (short s : shorts) {
sArray[i++] = s;
}
return sArray;
}
public static short[] toShortArray(byte[] data, boolean bigEndian) {
return toShortArray(data, 0, data.length, bigEndian);
}
public static short[] toShortArray(byte[] data, int offset, int len, boolean bigEndian) {
ByteBuffer byteBuffer = ByteBuffer.wrap(data, offset, len);
if (bigEndian) {
byteBuffer.order(ByteOrder.BIG_ENDIAN);
} else {
byteBuffer.order(ByteOrder.LITTLE_ENDIAN);
}
ShortBuffer shortBuf = byteBuffer.asShortBuffer();
short[] array = new short[shortBuf.remaining()];
shortBuf.get(array);
return array;
}
private ArrayUtils(){} // Prevents instantiation
}