matlabcontrol.link.ArrayUtils Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of matlabcontrol Show documentation
Show all versions of matlabcontrol Show documentation
MatConsoleCtl - control MATLAB from Java
/*
* Copyright (c) 2013, Joshua Kaplan
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification, are permitted provided that the
* following conditions are met:
* - Redistributions of source code must retain the above copyright notice, this list of conditions and the following
* disclaimer.
* - Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the
* following disclaimer in the documentation and/or other materials provided with the distribution.
* - Neither the name of matlabcontrol nor the names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
package matlabcontrol.link;
import java.lang.reflect.Array;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
/**
* Utility functions for working with and creating arrays. This class contains some of the functionality contained in
* {@link java.lang.Arrays}, but designed to work when the type of the array is not statically known.
*
* @since 4.2.0
* @author Joshua Kaplan
*/
class ArrayUtils {
private ArrayUtils() {}
/**
* Deeply copies a primitive array. If the array is not primitive then the {@code Object}s in the array will not
* be copies, although the arrays will be copied.
*
* @param
* @param array
* @return copy of array
*/
@SuppressWarnings("unchecked")
static T deepCopy(T array) {
T copy;
if (array == null) {
copy = null;
} else if (array.getClass().isArray()) {
//Array of arrays
if (array.getClass().getComponentType().isArray()) {
int arrayLength = Array.getLength(array);
copy = (T) Array.newInstance(array.getClass().getComponentType(), arrayLength);
for (int i = 0; i < arrayLength; i++) {
Array.set(copy, i, deepCopy(Array.get(array, i)));
}
}
//Array of values
else {
int arrayLength = Array.getLength(array);
copy = (T) Array.newInstance(array.getClass().getComponentType(), arrayLength);
System.arraycopy(array, 0, copy, 0, arrayLength);
}
} else {
throw new IllegalArgumentException("Input not an array: " + array.getClass().getCanonicalName());
}
return copy;
}
/**
* Computes the total number of elements in an array with dimensions specified by {@code dimensions}.
*
* @param dimensions
* @return
*/
static int getNumberOfElements(int[] dimensions) {
int size = 0;
for (int length : dimensions) {
if (size == 0) {
size = length;
} else if (length != 0) {
size *= length;
}
}
return size;
}
/**
* Multidimensional indices to linear index. Similar to MATLAB's (@code sub2ind} function. The lengths of
* {@code dimensions} and {@code indices} should be the same; this is not checked.
*
* @param dimensions the lengths of the array in each dimension
* @param indices
* @return linear index
*/
static int multidimensionalIndicesToLinearIndex(int[] dimensions, int[] indices) {
int linearIndex = 0;
int accumSize = 1;
for (int i = 0; i < dimensions.length; i++) {
linearIndex += accumSize * indices[i];
accumSize *= dimensions[i];
}
return linearIndex;
}
//Optimized version for 2 indices
static int multidimensionalIndicesToLinearIndex(int[] dimensions, int row, int column) {
return column * dimensions[0] + row;
}
static int multidimensionalIndicesToLinearIndex(int numRows, int row, int column) {
return column * numRows + row;
}
//Optimized version for 3 indices
static int multidimensionalIndicesToLinearIndex(int[] dimensions, int row, int column, int page) {
return page * (dimensions[0] * dimensions[1]) + column * dimensions[0] + row;
}
/**
* Multidimensional indices to linear index where there must be at least two indices {@code row} and {@code column}.
* The length of the {@code dimensions} and the indices must be the same, where the length of the indices is
* determined as {@code pages.length + 2}. Each index must be less than the length of the corresponding dimension.
*
* @param dimensions the dimensions of the array
* @param row the row index
* @param column the column index
* @param pages the zero or more page indices
* @return
* @throws IllegalArgumentException if the number of indices does not equal the number of dimensions
* @throws ArrayIndexOutOfBoundsException if the index is greater than or equal to the length of the corresponding
* dimension
*/
static int checkedMultidimensionalIndicesToLinearIndex(int[] dimensions, int row, int column, int pages[]) {
//Check the number of indices provided was correct
if (dimensions.length != pages.length + 2) {
throw new IllegalArgumentException("Array has " + dimensions.length + " dimension(s), it cannot be " +
"indexed into using " + (pages.length + 2) + " indices");
}
//Check the row index is in bounds
if (row >= dimensions[0]) {
throw new ArrayIndexOutOfBoundsException("[" + row + "] is out of bounds for dimension 0 where the " +
"length is " + dimensions[0]);
}
//Check the column index is in bounds
if (column >= dimensions[1]) {
throw new ArrayIndexOutOfBoundsException("[" + column + "] is out of bounds for dimension 1 where the " +
"length is " + dimensions[1]);
}
//Check the page indices are in bounds
for (int i = 0; i < pages.length; i++) {
if (pages[i] >= dimensions[i + 2]) {
throw new ArrayIndexOutOfBoundsException("[" + pages[i] + "] is out of bounds for dimension " +
(i + 2) + " where the length is " + dimensions[i + 2]);
}
}
//Convert the indices to a linear index
int linearIndex = 0;
int accumSize = 1;
//row
linearIndex += accumSize * row;
accumSize *= dimensions[0];
//column
linearIndex += accumSize * column;
accumSize *= dimensions[1];
//pages
for (int i = 0; i < pages.length; i++) {
linearIndex += accumSize * pages[i];
accumSize *= dimensions[i + 2];
}
return linearIndex;
}
//Optimized for 2 indices
static int checkedMultidimensionalIndicesToLinearIndex(int numRows, int numCols, int row, int column) {
//Check the row index is in bounds
if (row >= numRows) {
throw new ArrayIndexOutOfBoundsException("[" + row + "] is out of bounds for dimension 0 where the " +
"length is " + numRows);
}
//Check the column index is in bounds
if (column >= numCols) {
throw new ArrayIndexOutOfBoundsException("[" + column + "] is out of bounds for dimension 1 where the " +
"length is " + numCols);
}
return column * numRows + row;
}
//Optimized for 2 indices
static int checkedMultidimensionalIndicesToLinearIndex(int[] dimensions, int row, int column) {
//Check the row index is in bounds
if (row >= dimensions[0]) {
throw new ArrayIndexOutOfBoundsException("[" + row + "] is out of bounds for dimension 0 where the " +
"length is " + dimensions[0]);
}
//Check the column index is in bounds
if (column >= dimensions[1]) {
throw new ArrayIndexOutOfBoundsException("[" + column + "] is out of bounds for dimension 1 where the " +
"length is " + dimensions[1]);
}
return column * dimensions[0] + row;
}
//Optimized for 3 indices
static int checkedMultidimensionalIndicesToLinearIndex(int[] dimensions, int row, int column, int page) {
//Check the row index is in bounds
if (row >= dimensions[0]) {
throw new ArrayIndexOutOfBoundsException("[" + row + "] is out of bounds for dimension 0 where the " +
"length is " + dimensions[0]);
}
//Check the column index is in bounds
if (column >= dimensions[1]) {
throw new ArrayIndexOutOfBoundsException("[" + column + "] is out of bounds for dimension 1 where the " +
"length is " + dimensions[1]);
}
//Check the page index is in bounds
if (page >= dimensions[2]) {
throw new ArrayIndexOutOfBoundsException("[" + column + "] is out of bounds for dimension 2 where the " +
"length is " + dimensions[2]);
}
return page * (dimensions[0] * dimensions[1]) + column * dimensions[0] + row;
}
/**
* Linear index to multidimensional indices. Similar to MATLAB's (@code ind2sub} function.
*
* @param dimensions the lengths of the array in each dimension
* @param linearIndex
* @return
*/
static int[] linearIndexToMultidimensionalIndices(int[] dimensions, int linearIndex) {
int[] indices = new int[dimensions.length];
if (dimensions.length == 1) {
indices[0] = linearIndex;
} else {
int pageSize = dimensions[0] * dimensions[1];
int pageNumber = linearIndex / pageSize;
//Row and column
int indexInPage = linearIndex % pageSize;
indices[0] = indexInPage % dimensions[0];
indices[1] = indexInPage / dimensions[0];
//3rd dimension and above
int accumSize = 1;
for (int dim = 2; dim < dimensions.length; dim++) {
indices[dim] = (pageNumber / accumSize) % dimensions[dim];
accumSize *= dimensions[dim];
}
}
return indices;
}
/**
* Gets the base component type of {@code type} assuming {@code type} is a type of array. If {@code type} is not
* an array then the same class passed in will be returned. For example, if {@code type} is {@code boolean[][]} then
* {@code boolean} would be returned.
*
* @param type
* @return
*/
static Class> getBaseComponentType(Class> type) {
while (type.isArray()) {
type = type.getComponentType();
}
return type;
}
/**
* Gets the number of dimensions represented by {@code type}. If not an array then {@code 0} will be returned.
*
* @param type
* @return
*/
static int getNumberOfDimensions(Class> type) {
int numDim = 0;
while (type.isArray()) {
numDim++;
type = type.getComponentType();
}
return numDim;
}
/**
* Determines the maximum length for each dimension of the array.
*
* @param array
* @return
*/
static int[] computeBoundingDimensions(Object array) {
int[] maxLengths = new int[getNumberOfDimensions(array.getClass())];
//The length of this array
int arrayLength = Array.getLength(array);
maxLengths[0] = arrayLength;
//If the array holds arrays as its entries
if (array.getClass().getComponentType().isArray()) {
//For each entry in the array
for (int i = 0; i < arrayLength; i++) {
//childLengths' information will be one index ahead of maxLengths
int[] childLengths = computeBoundingDimensions(Array.get(array, i));
for (int j = 0; j < childLengths.length; j++) {
maxLengths[j + 1] = Math.max(maxLengths[j + 1], childLengths[j]);
}
}
}
return maxLengths;
}
/**
* Gets the class representing an array of type {@code componentType} with the number of dimensions specified by
* {@code rank}. JVMs typically impose a limit of 255 dimensions.
*
* @param componentType
* @param rank
* @return
*/
static Class> getArrayClass(Class> componentType, int rank) {
String binaryName;
if (componentType.isPrimitive()) {
binaryName = getPrimitiveArrayBinaryName(componentType, rank);
} else {
binaryName = getObjectArrayBinaryName(componentType, rank);
}
try {
return Class.forName(binaryName, false, componentType.getClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException("Could not create array class\n" +
"Component Type (Canonical Name): " + componentType.getCanonicalName() + "\n" +
"Component Type (Name): " + componentType.getName() + "\n" +
"Rank: " + rank + "\n" +
"Array Class Binary Name: " + binaryName, e);
}
}
private static String getPrimitiveArrayBinaryName(Class> componentType, int rank) {
//Build binary name
char[] nameChars = new char[rank + 1];
for (int i = 0; i < rank; i++) {
nameChars[i] = '[';
}
nameChars[nameChars.length - 1] = PRIMITIVE_TO_BINARY_NAME.get(componentType);
return new String(nameChars);
}
private static final Map, Character> PRIMITIVE_TO_BINARY_NAME;
static {
Map, Character> map = new HashMap, Character>();
map.put(byte.class, 'B');
map.put(short.class, 'S');
map.put(int.class, 'I');
map.put(long.class, 'J');
map.put(float.class, 'F');
map.put(double.class, 'D');
map.put(boolean.class, 'Z');
map.put(char.class, 'C');
PRIMITIVE_TO_BINARY_NAME = Collections.unmodifiableMap(map);
}
private static String getObjectArrayBinaryName(Class> componentType, int rank) {
String componentName = componentType.getName();
int componentNameLength = componentName.length();
char[] nameChars = new char[componentNameLength + rank + 2];
for (int i = 0; i < rank; i++) {
nameChars[i] = '[';
}
nameChars[rank] = 'L';
System.arraycopy(componentName.toCharArray(), 0, nameChars, rank + 1, componentNameLength);
nameChars[nameChars.length - 1] = ';';
return new String(nameChars);
}
/**
* Hashes an array. Provides a deep hash code in the case of an object array that contains other arrays.
*
* @param array must be an array of any type or {@code null}
* @return hashCode
*/
static int hashCode(Object array) {
int hashCode;
if (array == null) {
hashCode = 0;
} else if (array instanceof byte[]) {
hashCode = Arrays.hashCode((byte[]) array);
} else if (array instanceof short[]) {
hashCode = Arrays.hashCode((short[]) array);
} else if (array instanceof int[]) {
hashCode = Arrays.hashCode((int[]) array);
} else if (array instanceof long[]) {
hashCode = Arrays.hashCode((long[]) array);
} else if (array instanceof float[]) {
hashCode = Arrays.hashCode((float[]) array);
} else if (array instanceof double[]) {
hashCode = Arrays.hashCode((double[]) array);
} else if (array instanceof boolean[]) {
hashCode = Arrays.hashCode((boolean[]) array);
} else if (array instanceof char[]) {
hashCode = Arrays.hashCode((char[]) array);
}
//This is true if array is any non-primitive array
else if (array instanceof Object[]) {
hashCode = Arrays.hashCode((Object[]) array);
} else {
throw new IllegalArgumentException("value provided is not array, class: " +
array.getClass().getCanonicalName());
}
return hashCode;
}
static boolean equals(Object array1, Object array2) {
boolean equal = false;
//If the same array or both null
if (array1 == array2) {
equal = true;
}
//If both non-null and of the same class
else if (array1 != null && array2 != null && array1.getClass().equals(array2.getClass())) {
if (array1 instanceof byte[] && array2 instanceof byte[]) {
equal = Arrays.equals((byte[]) array1, (byte[]) array2);
} else if (array1 instanceof short[] && array2 instanceof short[]) {
equal = Arrays.equals((short[]) array1, (short[]) array2);
} else if (array1 instanceof int[] && array2 instanceof int[]) {
equal = Arrays.equals((int[]) array1, (int[]) array2);
} else if (array1 instanceof long[] && array2 instanceof long[]) {
equal = Arrays.equals((byte[]) array1, (byte[]) array2);
} else if (array1 instanceof float[] && array2 instanceof float[]) {
equal = Arrays.equals((float[]) array1, (float[]) array2);
} else if (array1 instanceof double[] && array2 instanceof double[]) {
equal = Arrays.equals((double[]) array1, (double[]) array2);
} else if (array1 instanceof boolean[] && array2 instanceof boolean[]) {
equal = Arrays.equals((boolean[]) array1, (boolean[]) array2);
} else if (array1 instanceof char[] && array2 instanceof char[]) {
equal = Arrays.equals((char[]) array1, (char[]) array2);
} else if (array1 instanceof Object[] && array2 instanceof Object[]) {
equal = Arrays.equals((Object[]) array1, (Object[]) array2);
} else {
throw new IllegalArgumentException("One or more of the values provided are not an array\n" +
"array1 class: " + array1.getClass() + "\n" +
"array2 class: " + array2.getClass());
}
}
return equal;
}
@SuppressWarnings({"unchecked", "rawtypes"})
static boolean containsNonDefaultValue(Object array) {
boolean contains;
if (array == null) {
throw new NullPointerException("array may not be null");
} else if (!array.getClass().isArray()) {
throw new IllegalArgumentException("value provided is not an array, class: " + array.getClass());
} else {
ArrayContainmentOperation operation;
if (array.getClass().getComponentType().isPrimitive()) {
operation = CONTAINMENT_OPERATIONS.get(array.getClass().getComponentType());
} else {
operation = CONTAINMENT_OPERATIONS.get(Object[].class);
}
contains = operation.containsNonDefaultValue(array);
}
return contains;
}
private static final Map, ArrayContainmentOperation>> CONTAINMENT_OPERATIONS;
static {
Map, ArrayContainmentOperation>> map = new HashMap, ArrayContainmentOperation>>();
map.put(byte[].class, new ByteArrayContainmentOperation());
map.put(short[].class, new ShortArrayContainmentOperation());
map.put(int[].class, new IntArrayContainmentOperation());
map.put(long[].class, new LongArrayContainmentOperation());
map.put(float[].class, new FloatArrayContainmentOperation());
map.put(double[].class, new DoubleArrayContainmentOperation());
map.put(boolean[].class, new BooleanArrayContainmentOperation());
map.put(char[].class, new CharArrayContainmentOperation());
map.put(Object[].class, new ObjectArrayContainmentOperation());
CONTAINMENT_OPERATIONS = Collections.unmodifiableMap(map);
}
private static interface ArrayContainmentOperation {
public boolean containsNonDefaultValue(T array);
}
private static class ByteArrayContainmentOperation implements ArrayContainmentOperation {
private static final byte DEFAULT_VAL = (byte) 0;
@Override
public boolean containsNonDefaultValue(byte[] array) {
boolean contains = false;
for (byte val : array) {
if (val != DEFAULT_VAL) {
contains = true;
break;
}
}
return contains;
}
}
private static class ShortArrayContainmentOperation implements ArrayContainmentOperation {
private static final short DEFAULT_VAL = (short) 0;
@Override
public boolean containsNonDefaultValue(short[] array) {
boolean contains = false;
for (short val : array) {
if (val != DEFAULT_VAL) {
contains = true;
break;
}
}
return contains;
}
}
private static class IntArrayContainmentOperation implements ArrayContainmentOperation {
private static final int DEFAULT_VAL = 0;
@Override
public boolean containsNonDefaultValue(int[] array) {
boolean contains = false;
for (int val : array) {
if (val != DEFAULT_VAL) {
contains = true;
break;
}
}
return contains;
}
}
private static class LongArrayContainmentOperation implements ArrayContainmentOperation {
private static final long DEFAULT_VAL = 0L;
@Override
public boolean containsNonDefaultValue(long[] array) {
boolean contains = false;
for (long val : array) {
if (val != DEFAULT_VAL) {
contains = true;
break;
}
}
return contains;
}
}
private static class FloatArrayContainmentOperation implements ArrayContainmentOperation {
private static final float DEFAULT_VAL = 0f;
@Override
public boolean containsNonDefaultValue(float[] array) {
boolean contains = false;
for (float val : array) {
if (val != DEFAULT_VAL) {
contains = true;
break;
}
}
return contains;
}
}
private static class DoubleArrayContainmentOperation implements ArrayContainmentOperation {
private static final double DEFAULT_VAL = 0d;
@Override
public boolean containsNonDefaultValue(double[] array) {
boolean contains = false;
for (double val : array) {
if (val != DEFAULT_VAL) {
contains = true;
break;
}
}
return contains;
}
}
private static class BooleanArrayContainmentOperation implements ArrayContainmentOperation {
private static final boolean DEFAULT_VAL = false;
@Override
public boolean containsNonDefaultValue(boolean[] array) {
boolean contains = false;
for (boolean val : array) {
if (val != DEFAULT_VAL) {
contains = true;
break;
}
}
return contains;
}
}
private static class CharArrayContainmentOperation implements ArrayContainmentOperation {
private static final char DEFAULT_VAL = '\0';
@Override
public boolean containsNonDefaultValue(char[] array) {
boolean contains = false;
for (char val : array) {
if (val != DEFAULT_VAL) {
contains = true;
break;
}
}
return contains;
}
}
private static class ObjectArrayContainmentOperation implements ArrayContainmentOperation
© 2015 - 2024 Weber Informatics LLC | Privacy Policy