
ucar.ma2.MAMath Maven / Gradle / Ivy
/*
* Copyright 1998-2014 University Corporation for Atmospheric Research/Unidata
*
* Portions of this software were developed by the Unidata Program at the
* University Corporation for Atmospheric Research.
*
* Access and use of this software shall impose the following obligations
* and understandings on the user. The user is granted the right, without
* any fee or cost, to use, copy, modify, alter, enhance and distribute
* this software, and any derivative works thereof, and its supporting
* documentation for any purpose whatsoever, provided that this entire
* notice appears in all copies of the software, derivative works and
* supporting documentation. Further, UCAR requests that the user credit
* UCAR/Unidata in any publications that result from the use of this
* software or in any product that includes this software. The names UCAR
* and/or Unidata, however, may not be used in any advertising or publicity
* to endorse or promote any products or commercial entity unless specific
* written permission is obtained from UCAR/Unidata. The user also
* understands that UCAR/Unidata is not obligated to provide the user with
* any support, consulting, training or assistance of any kind with regard
* to the use, operation and performance of this software nor to provide
* the user with any updates, revisions, new versions or "bug fixes."
*
* THIS SOFTWARE IS PROVIDED BY UCAR/UNIDATA "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 UCAR/UNIDATA BE LIABLE FOR ANY SPECIAL,
* INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
* FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
* NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
* WITH THE ACCESS, USE OR PERFORMANCE OF THIS SOFTWARE.
*/
package ucar.ma2;
import ucar.nc2.ft.grid.IsMissingEvaluator;
import ucar.nc2.util.Misc;
/**
* Element by element algebra on Arrays
*
* @author caron
* @see Index
*/
public class MAMath {
/**
* Add elements of two arrays together, allocating the result array.
* The result type and the operation type are taken from the type of a.
*
* @param a add values from here
* @param b add values from here
* @return result = a + b
* @throws IllegalArgumentException a and b are not conformable
* @throws UnsupportedOperationException dont support this data type yet
*/
public static Array add(Array a, Array b) throws IllegalArgumentException {
Array result = Array.factory(a.getElementType(), a.getShape());
if (a.getElementType() == double.class) {
addDouble(result, a, b);
} else
throw new UnsupportedOperationException();
return result;
}
/**
* Add elements of two arrays together as doubles, place sum in the result array.
* The values from the arrays a and b are converted to double (if needed),
* and the sum is converted to the type of result (if needed).
*
* @param result result array
* @param a operand
* @param b operand
* @throws IllegalArgumentException a,b,and result are not conformable
*/
public static void addDouble(Array result, Array a, Array b)
throws IllegalArgumentException {
if (!conformable(result, a) || !conformable(a, b))
throw new IllegalArgumentException();
IndexIterator iterR = result.getIndexIterator();
IndexIterator iterA = a.getIndexIterator();
IndexIterator iterB = b.getIndexIterator();
while (iterA.hasNext())
iterR.setDoubleNext(iterA.getDoubleNext() + iterB.getDoubleNext());
}
/**
* Check that two arrays are conformable.
*
* @param a operand
* @param b operand
* @return true if conformable
*/
public static boolean conformable(Array a, Array b) {
return conformable(a.getShape(), b.getShape());
}
/**
* Check that two array shapes are conformable.
* The shapes must match exactly, except that dimensions of length 1 are ignored.
*
* @param shapeA shape of array 1
* @param shapeB shape of array 2
* @return true if conformable
*/
public static boolean conformable(int[] shapeA, int[] shapeB) {
if (reducedRank(shapeA) != reducedRank(shapeB))
return false;
int rankB = shapeB.length;
int dimB = 0;
for (int aShapeA : shapeA) {
//System.out.println(dimA + " "+ dimB);
//skip length 1 dimensions
if (aShapeA == 1)
continue;
while (dimB < rankB)
if (shapeB[dimB] == 1) dimB++;
else break;
// test same shape (NB dimB cant be > rankB due to first test)
if (aShapeA != shapeB[dimB])
return false;
dimB++;
}
return true;
}
/**
* Convert unsigned data to signed data of a wider type.
*
* @param unsigned must be of type byte, short or int
* @return converted data of type short, int, or long
*/
public static Array convertUnsigned( Array unsigned) {
if (unsigned.getElementType().equals(byte.class)) {
Array result = Array.factory(DataType.SHORT, unsigned.getShape());
IndexIterator ii = result.getIndexIterator();
unsigned.resetLocalIterator();
while (unsigned.hasNext())
ii.setShortNext( DataType.unsignedByteToShort(unsigned.nextByte()));
return result;
} else if (unsigned.getElementType().equals(short.class)) {
Array result = Array.factory(DataType.INT, unsigned.getShape());
IndexIterator ii = result.getIndexIterator();
unsigned.resetLocalIterator();
while (unsigned.hasNext())
ii.setIntNext( DataType.unsignedShortToInt(unsigned.nextShort()));
return result;
} else if (unsigned.getElementType().equals(int.class)) {
Array result = Array.factory(DataType.LONG, unsigned.getShape());
IndexIterator ii = result.getIndexIterator();
unsigned.resetLocalIterator();
while (unsigned.hasNext())
ii.setLongNext( DataType.unsignedIntToLong(unsigned.nextInt()));
return result;
}
throw new IllegalArgumentException("Cant convertUnsigned type= "+unsigned.getElementType());
}
/**
* Convert original array to desired type
*
* @param org original array
* @param wantType desired type
* @return converted data of desired type, or original array if it is already
*/
public static Array convert( Array org, DataType wantType) {
if (org == null) return null;
Class wantClass = wantType.getPrimitiveClassType();
if (org.getElementType().equals(wantClass))
return org;
Array result = Array.factory(wantType, org.getShape());
copy(wantType, org.getIndexIterator(), result.getIndexIterator());
return result;
}
/**
* Copy using iterators. Will copy until !from.hasNext().
*
* @param dataType use this operation type (eg DataType.DOUBLE uses getDoubleNext())
* @param from copy from here
* @param to copy to here
* @throws IllegalArgumentException a and b are not conformable
* @throws UnsupportedOperationException dont support this data type
*/
public static void copy(DataType dataType, IndexIterator from, IndexIterator to) throws IllegalArgumentException {
if (dataType == DataType.DOUBLE) {
while (from.hasNext())
to.setDoubleNext(from.getDoubleNext());
} else if (dataType == DataType.FLOAT) {
while (from.hasNext())
to.setFloatNext(from.getFloatNext());
} else if (dataType == DataType.LONG) {
while (from.hasNext())
to.setLongNext(from.getLongNext());
} else if ((dataType == DataType.INT) || (dataType == DataType.ENUM4)) {
while (from.hasNext())
to.setIntNext(from.getIntNext());
} else if ((dataType == DataType.SHORT) || (dataType == DataType.ENUM2)) {
while (from.hasNext())
to.setShortNext(from.getShortNext());
} else if (dataType == DataType.CHAR) {
while (from.hasNext())
to.setCharNext(from.getCharNext());
} else if ((dataType == DataType.BYTE) || (dataType == DataType.ENUM1)) {
while (from.hasNext())
to.setByteNext(from.getByteNext());
} else if (dataType == DataType.BOOLEAN) {
while (from.hasNext())
to.setBooleanNext(from.getBooleanNext());
} else {
while (from.hasNext())
to.setObjectNext(from.getObjectNext());
}
}
/**
* Copy array a to array result, the result array will be in canonical order
* The operation type is taken from the type of a.
*
* @param result copy to here
* @param a copy from here
*
* @throws IllegalArgumentException a and b are not conformable
* @throws UnsupportedOperationException dont support this data type yet
*/
public static void copy(Array result, Array a) throws IllegalArgumentException {
Class classType = a.getElementType();
if (classType == double.class) {
copyDouble(result, a);
} else if (classType == float.class) {
copyFloat(result, a);
} else if (classType == long.class) {
copyLong(result, a);
} else if (classType == int.class) {
copyInt(result, a);
} else if (classType == short.class) {
copyShort(result, a);
} else if (classType == char.class) {
copyChar(result, a);
} else if (classType == byte.class) {
copyByte(result, a);
} else if (classType == boolean.class) {
copyBoolean(result, a);
} else
copyObject(result, a);
}
/**
* copy array a to array result as doubles
* The values from the arrays a are converted to double (if needed),
* and then converted to the type of result (if needed).
*
* @param result copy to here
* @param a copy from here
*
* @throws IllegalArgumentException a and result are not conformable
*/
public static void copyDouble(Array result, Array a) throws IllegalArgumentException {
if (!conformable(a, result))
throw new IllegalArgumentException("copy arrays are not conformable");
IndexIterator iterA = a.getIndexIterator();
IndexIterator iterR = result.getIndexIterator();
while (iterA.hasNext())
iterR.setDoubleNext(iterA.getDoubleNext());
}
/**
* copy array a to array result as floats
* The values from the arrays a are converted to float (if needed),
* and then converted to the type of result (if needed).
*
* @param result copy to here
* @param a copy from here
*
* @throws IllegalArgumentException a and result are not conformable
*/
public static void copyFloat(Array result, Array a) throws IllegalArgumentException {
if (!conformable(a, result))
throw new IllegalArgumentException("copy arrays are not conformable");
IndexIterator iterA = a.getIndexIterator();
IndexIterator iterR = result.getIndexIterator();
while (iterA.hasNext())
iterR.setFloatNext(iterA.getFloatNext());
}
/**
* copy array a to array result as longs
* The values from the array a are converted to long (if needed),
* and then converted to the type of result (if needed).
* @param result copy to here
* @param a copy from here
*
*
* @throws IllegalArgumentException a and result are not conformable
*/
public static void copyLong(Array result, Array a) throws IllegalArgumentException {
if (!conformable(a, result))
throw new IllegalArgumentException("copy arrays are not conformable");
IndexIterator iterA = a.getIndexIterator();
IndexIterator iterR = result.getIndexIterator();
while (iterA.hasNext())
iterR.setLongNext(iterA.getLongNext());
}
/**
* copy array a to array result as integers
* The values from the arrays a are converted to integer (if needed),
* and then converted to the type of result (if needed).
*
* @param result copy to here
* @param a copy from here
*
* @throws IllegalArgumentException a and result are not conformable
*/
public static void copyInt(Array result, Array a) throws IllegalArgumentException {
if (!conformable(a, result))
throw new IllegalArgumentException("copy arrays are not conformable");
IndexIterator iterA = a.getIndexIterator();
IndexIterator iterR = result.getIndexIterator();
while (iterA.hasNext())
iterR.setIntNext(iterA.getIntNext());
}
/**
* copy array a to array result as shorts
* The values from the array a are converted to short (if needed),
* and then converted to the type of result (if needed).
*
* @param result copy to here
* @param a copy from here
*
* @throws IllegalArgumentException a and result are not conformable
*/
public static void copyShort(Array result, Array a) throws IllegalArgumentException {
if (!conformable(a, result))
throw new IllegalArgumentException("copy arrays are not conformable");
IndexIterator iterA = a.getIndexIterator();
IndexIterator iterR = result.getIndexIterator();
while (iterA.hasNext())
iterR.setShortNext(iterA.getShortNext());
}
/**
* copy array a to array result as char
* The values from the array a are converted to char (if needed),
* and then converted to the type of result (if needed).
*
* @param result copy to here
* @param a copy from here
*
* @throws IllegalArgumentException a and result are not conformable
*/
public static void copyChar(Array result, Array a) throws IllegalArgumentException {
if (!conformable(a, result))
throw new IllegalArgumentException("copy arrays are not conformable");
IndexIterator iterA = a.getIndexIterator();
IndexIterator iterR = result.getIndexIterator();
while (iterA.hasNext())
iterR.setCharNext(iterA.getCharNext());
}
/**
* copy array a to array result as bytes
* The values from the array a are converted to byte (if needed),
* and then converted to the type of result (if needed).
*
* @param result copy to here
* @param a copy from here
*
* @throws IllegalArgumentException a and result are not conformable
*/
public static void copyByte(Array result, Array a) throws IllegalArgumentException {
if (!conformable(a, result))
throw new IllegalArgumentException("copy arrays are not conformable");
IndexIterator iterA = a.getIndexIterator();
IndexIterator iterR = result.getIndexIterator();
while (iterA.hasNext())
iterR.setByteNext(iterA.getByteNext());
}
/**
* copy array a to array result as bytes
* The array a and result must be type boolean
*
* @param result copy to here
* @param a copy from here
*
* @throws IllegalArgumentException a and result are not conformable
*/
public static void copyBoolean(Array result, Array a) throws IllegalArgumentException {
if (!conformable(a, result))
throw new IllegalArgumentException("copy arrays are not conformable");
IndexIterator iterA = a.getIndexIterator();
IndexIterator iterR = result.getIndexIterator();
while (iterA.hasNext())
iterR.setBooleanNext(iterA.getBooleanNext());
}
/**
* copy array a to array result as an Object
* The array a and result must be type object
*
* @param result copy to here
* @param a copy from here
*
* @throws IllegalArgumentException a and result are not conformable
*/
public static void copyObject(Array result, Array a) throws IllegalArgumentException {
if (!conformable(a, result))
throw new IllegalArgumentException("copy arrays are not conformable");
IndexIterator iterA = a.getIndexIterator();
IndexIterator iterR = result.getIndexIterator();
while (iterA.hasNext())
iterR.setObjectNext(iterA.getObjectNext());
}
/**
* Calculate the reduced rank of this shape, by subtracting dimensions with length 1
* @param shape shape of the array
* @return rank without dimensions of length 1
*/
public static int reducedRank(int[] shape) {
int rank = 0;
for (int aShape : shape) {
if (aShape > 1)
rank++;
}
return rank;
}
public static double getMinimum(Array a) {
IndexIterator iter = a.getIndexIterator();
double min = Double.MAX_VALUE;
while (iter.hasNext()) {
double val = iter.getDoubleNext();
if (Double.isNaN(val)) continue;
if (val < min)
min = val;
}
return min;
}
public static double getMaximum(Array a) {
IndexIterator iter = a.getIndexIterator();
double max = -Double.MAX_VALUE;
while (iter.hasNext()) {
double val = iter.getDoubleNext();
if (Double.isNaN(val)) continue;
if (val > max)
max = val;
}
return max;
}
/**
* Find min and max value in this array, getting values as doubles. Skip Double.NaN.
*
* @param a the array.
* @return MinMax
*/
public static MAMath.MinMax getMinMax(Array a) {
IndexIterator iter = a.getIndexIterator();
double max = -Double.MAX_VALUE;
double min = Double.MAX_VALUE;
while (iter.hasNext()) {
double val = iter.getDoubleNext();
if (Double.isNaN(val)) continue;
if (val > max)
max = val;
if (val < min)
min = val;
}
return new MinMax(min, max);
}
public static MAMath.MinMax getMinMaxSkipMissingData(Array a, IsMissingEvaluator eval) {
if (!eval.hasMissing())
return MAMath.getMinMax(a);
IndexIterator iter = a.getIndexIterator();
double max = -Double.MAX_VALUE;
double min = Double.MAX_VALUE;
while (iter.hasNext()) {
double val = iter.getDoubleNext();
if (eval.isMissing(val))
continue;
if (val > max)
max = val;
if (val < min)
min = val;
}
return new MAMath.MinMax(min, max);
}
public static double getMinimumSkipMissingData(Array a, double missingValue) {
IndexIterator iter = a.getIndexIterator();
double min = Double.MAX_VALUE;
while (iter.hasNext()) {
double val = iter.getDoubleNext();
if ((val != missingValue) && (val < min))
min = val;
}
return min;
}
public static double getMaximumSkipMissingData(Array a, double missingValue) {
IndexIterator iter = a.getIndexIterator();
double max = -Double.MAX_VALUE;
while (iter.hasNext()) {
double val = iter.getDoubleNext();
if ((val != missingValue) && (val > max))
max = val;
}
return max;
}
public static MAMath.MinMax getMinMaxSkipMissingData(Array a, double missingValue) {
IndexIterator iter = a.getIndexIterator();
double max = -Double.MAX_VALUE;
double min = Double.MAX_VALUE;
while (iter.hasNext()) {
double val = iter.getDoubleNext();
if (val == missingValue)
continue;
if (val > max)
max = val;
if (val < min)
min = val;
}
return new MinMax(min, max);
}
/**
* Set all the elements of this array to the given double value.
* The value is converted to the element type of the array, if needed.
*
* @param result change this Array
* @param val set all elements to this value
*/
public static void setDouble(Array result, double val) {
IndexIterator iter = result.getIndexIterator();
while (iter.hasNext()) {
iter.setDoubleNext(val);
}
}
/**
* sum all of the elements of array a as doubles.
* The values from the array a are converted to double (if needed).
* @param a read values from this Array
* @return sum of elements
*/
public static double sumDouble(Array a) {
double sum = 0;
IndexIterator iterA = a.getIndexIterator();
while (iterA.hasNext()) {
sum += iterA.getDoubleNext();
}
return sum;
}
/**
* sum all of the elements of array a as doubles.
* The values from the array a are converted to double (if needed).
* @param a read values from this Array
* @param missingValue skip values equal to this, or which are NaNs
* @return sum of elements
*/
public static double sumDoubleSkipMissingData(Array a, double missingValue) {
double sum = 0;
IndexIterator iterA = a.getIndexIterator();
while (iterA.hasNext()) {
double val = iterA.getDoubleNext();
if ((val == missingValue) || Double.isNaN(val))
continue;
sum += val;
}
return sum;
}
/**
* Holds a minimum and maximum value.
*/
public static class MinMax {
public double min, max;
public MinMax(double min, double max) {
this.min = min;
this.max = max;
}
@Override
public String toString() {
return "MinMax{" +
"min=" + min +
", max=" + max +
'}';
}
}
/**
* Calculate the scale/offset for an array of numbers.
*
* If signed:
* then
* max value unpacked = 2^(n-1) - 1 packed
* min value unpacked = -(2^(n-1) - 1) packed
* note that -2^(n-1) is unused, and a good place to map missing values
* by solving 2 eq in 2 unknowns, we get:
* scale = (max - min) / (2^n - 2)
* offset = (max + min) / 2
* If unsigned then
* max value unpacked = 2^n - 1 packed
* min value unpacked = 0 packed
* and:
* scale = (max - min) / (2^n - 1)
* offset = min
* One could modify this to allow a holder for missing values.
*
* @param a array to convert (not changed)
* @param missingValue skip these
* @param nbits map into this many bits
* @param isUnsigned use signed or unsigned packed values
* @return ScaleOffset, calculated as above.
*/
public static MAMath.ScaleOffset calcScaleOffsetSkipMissingData(Array a, double missingValue, int nbits, boolean isUnsigned) {
MAMath.MinMax minmax = getMinMaxSkipMissingData(a, missingValue);
if (isUnsigned) {
long size = (1L << nbits) - 1;
double offset = minmax.min;
double scale =(minmax.max - minmax.min) / size;
return new ScaleOffset(scale, offset);
} else {
long size = (1L << nbits) - 2;
double offset = (minmax.max + minmax.min) / 2;
double scale =(minmax.max - minmax.min) / size;
return new ScaleOffset(scale, offset);
}
}
public static Array convert2packed(Array unpacked, double missingValue, int nbits, boolean isUnsigned, DataType packedType) {
MAMath.ScaleOffset scaleOffset = calcScaleOffsetSkipMissingData(unpacked, missingValue, nbits, isUnsigned);
Array result = Array.factory(packedType, unpacked.getShape());
IndexIterator riter = result.getIndexIterator();
while (unpacked.hasNext()) {
double uv = unpacked.nextDouble();
double pv = (uv - scaleOffset.offset) / scaleOffset.scale;
riter.setDoubleNext( pv);
}
return result;
}
public static Array convert2Unpacked(Array packed, ScaleOffset scaleOffset) {
//boolean isUnsigned = packed.isUnsigned();
Array result = Array.factory(DataType.DOUBLE, packed.getShape());
IndexIterator riter = result.getIndexIterator();
while (packed.hasNext()) {
riter.setDoubleNext( packed.nextDouble() * scaleOffset.scale + scaleOffset.offset);
}
return result;
}
/**
* Holds a scale and offset.
*/
public static class ScaleOffset {
public double scale, offset;
public ScaleOffset(double scale, double offset) {
this.scale = scale;
this.offset = offset;
}
}
public static boolean isEqual(Array data1, Array data2) {
if (data1.getSize() != data2.getSize()) return false;
DataType dt = DataType.getType(data1.getElementType());
IndexIterator iter1 = data1.getIndexIterator();
IndexIterator iter2 = data2.getIndexIterator();
if (dt == DataType.DOUBLE) {
while (iter1.hasNext() && iter2.hasNext()) {
double v1 = iter1.getDoubleNext();
double v2 = iter2.getDoubleNext();
if (!Double.isNaN(v1) || !Double.isNaN(v2))
if (!Misc.closeEnough(v1, v2, 1.0e-8))
return false;
}
} else if (dt == DataType.FLOAT) {
while (iter1.hasNext() && iter2.hasNext()) {
float v1 = iter1.getFloatNext();
float v2 = iter2.getFloatNext();
if (!Float.isNaN(v1) || !Float.isNaN(v2))
if (!Misc.closeEnough(v1, v2, 1.0e-5))
return false;
}
} else if (dt == DataType.INT) {
while (iter1.hasNext() && iter2.hasNext()) {
int v1 = iter1.getIntNext();
int v2 = iter2.getIntNext();
if (v1 != v2) return false;
}
} else if (dt == DataType.SHORT) {
while (iter1.hasNext() && iter2.hasNext()) {
short v1 = iter1.getShortNext();
short v2 = iter2.getShortNext();
if (v1 != v2) return false;
}
} else if (dt == DataType.BYTE) {
while (iter1.hasNext() && iter2.hasNext()) {
byte v1 = iter1.getByteNext();
byte v2 = iter2.getByteNext();
if (v1 != v2) return false;
}
} else if (dt == DataType.LONG) {
while (iter1.hasNext() && iter2.hasNext()) {
long v1 = iter1.getLongNext();
long v2 = iter2.getLongNext();
if (v1 != v2) return false;
}
}
return true;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy