MathMethod.java
package fr.inrae.agroclim.indicators.model.function.aggregation;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import fr.inrae.agroclim.indicators.model.JEXLFormula;
/**
* Aggregation methods for {@link JEXLFormula}.
*
* @author Olivier Maury
*/
public class MathMethod {
/**
* Returns the average of the values.
*
* @param values
* @return average value
*/
public static Double avg(final Number... values) {
int nb = 0;
Double sum = 0.;
for (final Number value : values) {
if (value == null) {
continue;
}
sum += value.doubleValue();
nb++;
}
if (nb == 0) {
return null;
}
return sum / nb;
}
/**
* Returns Euler's number <i>e</i> raised to the power of a
* {@code double} value. Special cases:
* <ul><li>If the argument is NaN, the result is NaN.
* <li>If the argument is positive infinity, then the result is
* positive infinity.
* <li>If the argument is negative infinity, then the result is
* positive zero.
* <li>If the argument is zero, then the result is {@code 1.0}.
* </ul>
*
* <p>The computed result must be within 1 ulp of the exact result.
* Results must be semi-monotonic.
*
* @param a the exponent to raise <i>e</i> to.
* @return the value <i>e</i><sup>{@code a}</sup>,
* where <i>e</i> is the base of the natural logarithms.
*/
public static Double exp(final Number a) {
if (isNaN(a)) {
return null;
}
return Math.exp(a.doubleValue());
}
/**
* List all available methods.
*
* @return method name ⮕ method representation with parameters
*/
public static Map<String, String> getMethodNamesWithParameters() {
final Map<String, String> map = new HashMap<>();
final Method[] methods = MathMethod.class.getDeclaredMethods();
for (final Method method : methods) {
final Class<?>[] parameters = method.getParameterTypes();
if (parameters == null || parameters.length == 0
|| "isNaN".equals(method.getName()) || method.getName().startsWith("$")) {
continue;
}
final String val;
if (parameters.length == 1) {
val = "math:" + method.getName() + "(?)";
} else {
val = "math:" + method.getName() + "(?, ...)";
}
map.put(method.getName(), val);
}
return map;
}
private static boolean isNaN(final Number value) {
if (value instanceof Float floatValue) {
return Float.isNaN(floatValue);
}
return false;
}
/**
* Returns the natural logarithm (base <i>e</i>) of a {@code double}
* value. Special cases:
* <ul><li>If the argument is NaN or less than zero, then the result
* is NaN.
* <li>If the argument is positive infinity, then the result is
* positive infinity.
* <li>If the argument is positive zero or negative zero, then the
* result is negative infinity.
* <li>If the argument is {@code 1.0}, then the result is positive
* zero.
* </ul>
*
* <p>The computed result must be within 1 ulp of the exact result.
* Results must be semi-monotonic.
*
* @param a a value
* @return the value ln {@code a}, the natural logarithm of
* {@code a}.
*/
public static Double log(final Number a) {
if (isNaN(a)) {
return null;
}
return Math.log(a.doubleValue());
}
/**
* Returns the greater of Number values.
*
* That is, the result is the argument closer to positive infinity.<br>
* If the arguments have the same value, the result is that same value.<br>
* If either value is NaN, then the result is NaN.<br>
* Unlike the numerical comparison operators, this method considers negative
* zero to be strictly smaller than positive zero. If one argument is positive
* zero and the other negative zero, the result is positive zero.
*
* @param values arguments
* @return greatest of values.
*/
public static Double max(final Number... values) {
Double max = null;
for (final Number val : values) {
if (val == null || isNaN(val)) {
continue;
}
if (max == null || val.doubleValue() > max) {
max = val.doubleValue();
}
}
return max;
}
/**
* Returns the smaller of Number values.
*
* That is, the result is the value closer to negative infinity.<br>
* If the arguments have the same value, the result is that same value.<br>
* If either value is NaN, then the result is NaN.<br>
* Unlike the numerical comparison operators, this method considers negative
* zero to be strictly smaller than positive zero. If one argument is positive
* zero and the other is negative zero, the result is negative zero.
*
* @param values
* @return smallest of values.
*/
public static Double min(final Number... values) {
Double min = null;
for (final Number val : values) {
if (val == null || isNaN(val)) {
continue;
}
if (min == null || val.doubleValue() < min) {
min = val.doubleValue();
}
}
return min;
}
/**
* Returns the value of the first argument raised to the power of the second argument.
*
* Special cases, see {@java.lang.Math#pow(double, double)}.
*
* @param a the base
* @param b the exponent
* @return the value aᵇ.
*/
public static Double pow(final Number a, final Number b) {
if (a == null) {
return null;
}
if (b == null) {
return null;
}
double aValue = a.doubleValue();
double bValue = b.doubleValue();
return Math.pow(aValue, bValue);
}
/**
* Empty constructor to inject the class into JEXL.
*/
public MathMethod() {
// Do nothing
}
}