FunctionsMisc.java
/*
* Copyright 2016-2018 Federico Vera
*
* 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.
*/
package net.objecthunter.exp4j.extras;
import java.io.Serial;
import java.math.BigInteger;
import net.objecthunter.exp4j.function.Function;
import net.objecthunter.exp4j.function.Functions;
import net.objecthunter.exp4j.operator.Operator;
/**
* This class contains a small set of useful functions that don't really fit in
* the other categories.
*
* @author Federico Vera {@literal <[email protected]>}
* @since 0.6-riddler
*/
public final class FunctionsMisc {
/**
* Equality function.
*
* <p>The test is performed using a threshold given by
* {@link Operator#BOOLEAN_THRESHOLD}.</p>
* <p>This function has two argument {@code equal(a, b)} where:</p>
* <ul>
* <li><code><b>a</b></code>: First value to test</li>
* <li><code><b>b</b></code>: Second value to test</li>
* </ul>
*
* @see Operator#BOOLEAN_THRESHOLD
* @since 0.6-riddler
*/
public static final Function EQUAL = new Equals();
/**
* Branching function.
* <p>This function has three argument {@code if(exp, v_true, v_false)}
* where:</p>
* <ul>
* <li><code><b>exp</b></code>: Boolean expression</li>
* <li><code><b>v_true</b></code>: Value if true</li>
* <li><code><b>v_false</b></code>: Value if false</li>
* </ul>
*
* @see FunctionsBoolean
* @see OperatorsComparison
* @see Operator#BOOLEAN_THRESHOLD
* @see OperatorsComparison#OP_EQU
* @see OperatorsComparison#OP_GOE
* @see OperatorsComparison#OP_GT
* @see OperatorsComparison#OP_LOE
* @see OperatorsComparison#OP_LT
* @see OperatorsComparison#OP_NEQ
* @since 0.6-riddler
*/
public static final Function IF = new If();
/**
* Retrieves the value of {@link Double#POSITIVE_INFINITY}.
*
* @see Double#POSITIVE_INFINITY
* @see Double#NEGATIVE_INFINITY
* @since 0.8-riddler
*/
public static final Function INFINITY = new Infinity();
/**
* Tells if a number is {@link Double#NaN}.
*
* @see Double#isNaN(double)
* @since 0.8-riddler
*/
public static final Function IS_NAN = new IsNaN();
/**
* Returns the smallest (closest to negative infinity) of two numbers.
*
* @see Math#min(double, double)
* @since 0.8-riddler
*/
public static final Function MIN = new Min();
/**
* Returns the largest (closest to positive infinity) of two numbers.
*
* @see Math#max(double, double)
* @since 0.8-riddler
*/
public static final Function MAX = new Max();
/**
* Returns the Greatest Common Denominator of two numbers.
*
* <p>The numbers WILL be rounded using {@link Math#round(double)} before
* the analysis.</p>
* <p>If the resulting value is out of the range of the {@code long} type,
* then an {@code ArithmeticException} is thrown.</p>
*
* @see Functions#FLOOR
* @see Functions#CEIL
* @see FunctionsMisc#ROUND
* @see FunctionsMisc#LCM
* @since 0.8-riddler
*/
public static final Function GCD = new GCD();
/**
* Returns the Least Common Multiple of two numbers.
*
* <p>The numbers WILL be rounded using {@link Math#round(double)} before
* the analysis.</p>
* <p>If the resulting value is out of the range of the {@code long} type,
* then an {@code ArithmeticException} is thrown.</p>
*
* @see Functions#FLOOR
* @see Functions#CEIL
* @see FunctionsMisc#ROUND
* @see FunctionsMisc#GCD
* @since 0.8-riddler
*/
public static final Function LCM = new LCM();
/**
* Rounds to the closest integer.
*
* @see Functions#FLOOR
* @see Functions#CEIL
* @see Math#round(double)
* @since 0.8-riddler
*/
public static final Function ROUND = new Round();
/**
* Converts from degrees to radians.
*
* @see FunctionsMisc#RAD2DEG
* @see Math#toRadians(double)
* @since 0.9-riddler
*/
public static final Function DEG2RAD = new Deg2Rad();
/**
* Converts from radians to degrees.
*
* @see FunctionsMisc#DEG2RAD
* @see Math#toDegrees(double)
* @since 0.9-riddler
*/
public static final Function RAD2DEG = new Rad2Deg();
/**
* This is the threshold used to consider values equal, that is, if two
* values {@code a} and {@code b} are separated by less than this threshold
* they will be considered to be equal, it has a default value of {@value}
*/
public static final double EQUALITY_THRESHOLD = Operator.BOOLEAN_THRESHOLD;
/**
* Array with all the available functions.
*
* @return {@link Function} array
*
* @see FunctionsMisc#getFunction(String)
* @see FunctionsMisc#EQUAL
* @see FunctionsMisc#IF
* @see FunctionsMisc#INFINITY
* @see FunctionsMisc#IS_NAN
* @see FunctionsMisc#MIN
* @see FunctionsMisc#MAX
* @see FunctionsMisc#GCD
* @see FunctionsMisc#LCM
* @see FunctionsMisc#ROUND
* @see FunctionsMisc#DEG2RAD
* @see FunctionsMisc#RAD2DEG
*/
public static Function[] getFunctions() {
return new Function[]{
EQUAL, IF, INFINITY, IS_NAN, MIN, MAX, GCD, LCM, ROUND, DEG2RAD, RAD2DEG
};
}
/**
* Get the function for a given name.
*
* @param name the name of the function
* @return a Function instance
*
* @see FunctionsMisc#getFunctions()
* @see FunctionsMisc#EQUAL
* @see FunctionsMisc#IF
* @see FunctionsMisc#INFINITY
* @see FunctionsMisc#IS_NAN
* @see FunctionsMisc#MIN
* @see FunctionsMisc#MAX
* @see FunctionsMisc#GCD
* @see FunctionsMisc#LCM
* @see FunctionsMisc#ROUND
* @see FunctionsMisc#DEG2RAD
* @see FunctionsMisc#RAD2DEG
*/
public static Function getFunction(final String name) {
return switch (name) {
case "equal" -> EQUAL;
case "if" -> IF;
case "inf" -> INFINITY;
case "isnan" -> IS_NAN;
case "min" -> MIN;
case "max" -> MAX;
case "gcd" -> GCD;
case "lcm" -> LCM;
case "round" -> ROUND;
case "degtorad" -> DEG2RAD;
case "radtodeg" -> RAD2DEG;
default -> null;
};
}
private FunctionsMisc() {
// Don't let anyone initialize this class
}
private static final class Equals extends Function {
@Serial
private static final long serialVersionUID = 2388827649030518290L;
Equals() { super("equal", 2); }
@Override
public double apply(double... args) {
final double a = args[0];
final double b = args[1];
return Math.abs(a - b) < EQUALITY_THRESHOLD ? 1 : 0;
}
}
private static final class If extends Function {
@Serial
private static final long serialVersionUID = 3865326455639650003L;
If() { super("if", 3); }
@Override
public double apply(double... args) {
final boolean a = args[0] >= EQUALITY_THRESHOLD;
final double t = args[1];
final double f = args[2];
return a ? t : f;
}
}
private static final class Infinity extends Function {
@Serial
private static final long serialVersionUID = 6249177625376818393L;
Infinity() { super("inf", 0); }
@Override
public double apply(double... args) {
return Double.POSITIVE_INFINITY;
}
}
private static final class IsNaN extends Function {
@Serial
private static final long serialVersionUID = 2987603422726499329L;
IsNaN() { super("isnan", 1); }
@Override
public double apply(double... args) {
final double val = args[0];
return Double.isNaN(val) ? 1.0 : 0.0;
}
}
private static final class Min extends Function {
@Serial
private static final long serialVersionUID = -8343244242397439087L;
Min() { super("min", 2); }
@Override
public double apply(double... args) {
final double v1 = args[0];
final double v2 = args[1];
return Math.min(v1, v2);
}
}
private static final class Max extends Function {
@Serial
private static final long serialVersionUID = 426041154853511222L;
Max() { super("max", 2); }
@Override
public double apply(double... args) {
final double v1 = args[0];
final double v2 = args[1];
return Math.max(v1, v2);
}
}
private static final class GCD extends Function {
@Serial
private static final long serialVersionUID = -6539620489548306830L;
GCD() { super("gcd", 2); }
@Override
public double apply(double... args) {
final BigInteger v1 = BigInteger.valueOf(Math.round(args[0]));
final BigInteger v2 = BigInteger.valueOf(Math.round(args[1]));
return v1.gcd(v2).longValueExact();
}
}
private static final class LCM extends Function {
@Serial
private static final long serialVersionUID = -6539620489548306830L;
LCM() { super("lcm", 2); }
@Override
public double apply(double... args) {
final long a = Math.round(args[0]);
final long b = Math.round(args[1]);
final BigInteger v1 = BigInteger.valueOf(a);
final BigInteger v2 = BigInteger.valueOf(b);
final double gcd = v1.gcd(v2).longValueExact();
return Math.abs(a * (b / gcd ));
}
}
private static final class Round extends Function {
@Serial
private static final long serialVersionUID = -6539620489548306830L;
Round() { super("round", 1); }
@Override
public double apply(double... args) {
final double a = args[0];
return Math.round(a);
}
}
private static final class Deg2Rad extends Function {
@Serial
private static final long serialVersionUID = -6539620489548306830L;
Deg2Rad() { super("degtorad", 1); }
@Override
public double apply(double... args) {
final double x = args[0];
return Math.toRadians(x);
}
}
private static final class Rad2Deg extends Function {
@Serial
private static final long serialVersionUID = -6539620489548306830L;
Rad2Deg() { super("radtodeg", 1); }
@Override
public double apply(double... args) {
final double x = args[0];
return Math.toDegrees(x);
}
}
}