Java "ComputeElem"

Admin User, erstellt 11. Apr. 2024
         
package nova.envir;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* Warranty & Liability
* To the extent permitted by applicable law and unless explicitly
* otherwise agreed upon, XLOG Technologies AG makes no warranties
* regarding the provided information. XLOG Technologies AG assumes
* no liability that any problems might be solved with the information
* provided by XLOG Technologies AG.
* <p/>
* Rights & License
* All industrial property rights regarding the information - copyright
* and patent rights in particular - are the sole property of XLOG
* Technologies AG. If the company was not the originator of some
* excerpts, XLOG Technologies AG has at least obtained the right to
* reproduce, change and translate the information.
* <p/>
* Reproduction is restricted to the whole unaltered document. Reproduction
* of the information is only allowed for non-commercial uses. Selling,
* giving away or letting of the execution of the library is prohibited.
* The library can be distributed as part of your applications and libraries
* for execution provided this comment remains unchanged.
* <p/>
* Restrictions
* Only to be distributed with programs that add significant and primary
* functionality to the library. Not to be distributed with additional
* software intended to replace any components of the library.
* <p/>
* Trademarks
* Jekejeke is a registered trademark of XLOG Technologies AG.
*/
public final class ComputeElem {
public final static String OP_EVALUATION_ZERO_DIVISOR = "zero_divisor"; /* arithmetic */
public final static String OP_EVALUATION_FLOAT_OVERFLOW = "float_overflow";
public final static String OP_EVALUATION_UNDEFINED = "undefined";
public static final String OP_DOMAIN_NOT_LESS_THAN_ZERO = "not_less_than_zero";
public static final String OP_PERMISSION_NOT_SUPPORTED = "not_supported";
public final static BigInteger MIN_INTEGER = BigInteger.valueOf(Integer.MIN_VALUE);
public final static BigInteger MAX_INTEGER = BigInteger.valueOf(Integer.MAX_VALUE);
public final static Double ZERO_DOUBLE = Double.valueOf(0.0);
public final static BigInteger NEG_MIN_INTEGER = BigInteger.valueOf(-(long) Integer.MIN_VALUE);
public static final int NUM_INTEGER = 0;
public static final int NUM_BIG_INTEGER = 1;
public static final int NUM_DOUBLE = 2;
/**
* Widen a Prolog integer.
*
* @param num The Prolog integer.
* @return bigint The JavaScript bigint.
*/
public static BigInteger widen_bigint(Number num) {
if (num instanceof BigInteger) {
return (BigInteger) num;
} else {
return BigInteger.valueOf(num.intValue());
}
}
/**
* <p>Return the Prolog integer corresponding to the given long.</p>
*
* @param val The long.
* @return The Prolog integer.
*/
public static Number norm_smallint(long val) {
if (Integer.MIN_VALUE <= val && val <= Integer.MAX_VALUE) {
return Integer.valueOf((int) val);
} else {
return BigInteger.valueOf(val);
}
}
/**
* <p>Return the Prolog integer corresponding to the given big integer.</p>
*
* @param val The bigint.
* @return number The Prolog integer.
*/
public static Number norm_bigint(BigInteger val) {
if (MIN_INTEGER.compareTo(val) <= 0 && val.compareTo(MAX_INTEGER) <= 0) {
return Integer.valueOf(val.intValue());
} else {
return val;
}
}
/**
* Narrow a Prolog number to a float.
*
* @param num The Prolog number.
* @return number The JavaScript float.
*/
public static double narrow_float(Number num) {
if (num instanceof Double)
return num.doubleValue();
double val = num.doubleValue();
if (Double.isInfinite(val))
throw new ArithmeticException(OP_EVALUATION_FLOAT_OVERFLOW);
if (val == 0.0)
return 0.0;
return val;
}
/**
* <p>Create a double object.</p>
*
* @param val The Java float.
* @return number The Prolog float.
*/
public static Double norm_float(double val) {
if (Double.isNaN(val))
throw new ArithmeticException(OP_EVALUATION_UNDEFINED);
if (Double.isInfinite(val))
throw new ArithmeticException(OP_EVALUATION_FLOAT_OVERFLOW);
if (val == 0.0)
return ZERO_DOUBLE;
return Double.valueOf(val);
}
/**
* <p>Determine the category of a Prolog number.</p>
*
* @param m The Prolog number.
* @return The category.
*/
public static int numType(Number m) {
if (m instanceof Integer) {
return NUM_INTEGER;
} else if (m instanceof BigInteger) {
return NUM_BIG_INTEGER;
} else if (m instanceof Double) {
return NUM_DOUBLE;
} else {
throw new UnsupportedOperationException(OP_PERMISSION_NOT_SUPPORTED);
}
}
/********************************************************************/
/* Basic Arithmetical Operations: */
/* -/1: negate() */
/********************************************************************/
/**
* <p>Negate the Prolog number.</p>
*
* @param m The Prolog number.
* @return The negated Prolog number.
*/
public static Number negate(Number m) {
if (m instanceof Integer) {
int x = m.intValue();
if (x != Integer.MIN_VALUE) {
return Integer.valueOf(-x);
} else {
return BigInteger.valueOf(-(long) x);
}
} else if (m instanceof BigInteger) {
return norm_bigint(((BigInteger) m).negate());
} else if (m instanceof Float) {
return Float.valueOf(-m.floatValue());
} else if (m instanceof Double) {
return Double.valueOf(-m.doubleValue());
} else if (m instanceof BigDecimal) {
return ((BigDecimal) m).negate();
} else {
throw new UnsupportedOperationException(OP_PERMISSION_NOT_SUPPORTED);
}
}
/********************************************************************/
/* Basic Arithmetical Operations: */
/* +/2: add() */
/* -/2: subtract() */
/* * /2: multiply() */
/* //2: divide() */
/********************************************************************/
/**
* <p>Add the two Prolog numbers.</p>
*
* @param m The first Prolog number.
* @param n The second Prolog number.
* @return The sum of the two numbers.
*/
public static Number add(Number m, Number n) {
switch (Math.max(numType(m), numType(n))) {
case NUM_INTEGER:
return norm_smallint((long) m.intValue() + n.intValue());
case NUM_BIG_INTEGER:
return norm_bigint(widen_bigint(m).add(
widen_bigint(n)));
case NUM_DOUBLE:
return norm_float(narrow_float(m) +
narrow_float(n));
default:
throw new UnsupportedOperationException(OP_PERMISSION_NOT_SUPPORTED);
}
}
/**
* <p>Subtract the two Prolog numbers.</p>
*
* @param m The first Prolog number.
* @param n The second Prolog number.
* @return The first number subtracted by the second number.
*/
public static Number subtract(Number m, Number n) {
switch (Math.max(numType(m), numType(n))) {
case NUM_INTEGER:
return norm_smallint((long) m.intValue() - n.intValue());
case NUM_BIG_INTEGER:
return norm_bigint(widen_bigint(m).subtract(
widen_bigint(n)));
case NUM_DOUBLE:
return norm_float(narrow_float(m) -
narrow_float(n));
default:
throw new UnsupportedOperationException(OP_PERMISSION_NOT_SUPPORTED);
}
}
/**
* <p>Multiply the two Prolog numbers.</p>
*
* @param m The first Prolog number.
* @param n The second Prolog number.
* @return The product of the two numbers.
*/
public static Number multiply(Number m, Number n) {
switch (Math.max(numType(m), numType(n))) {
case NUM_INTEGER:
return norm_smallint((long) m.intValue() * n.intValue());
case NUM_BIG_INTEGER:
return norm_bigint(widen_bigint(m).multiply(
widen_bigint(n)));
case NUM_DOUBLE:
return norm_float(narrow_float(m) *
narrow_float(n));
default:
throw new UnsupportedOperationException(OP_PERMISSION_NOT_SUPPORTED);
}
}
/**
* <p>Slash the two Prolog numbers.</p>
*
* @param m The first Prolog number.
* @param n The second Prolog number.
* @return The first number slashed by the second number.
*/
public static Number divide(Number m, Number n) {
switch (Math.max(numType(m), numType(n))) {
case NUM_INTEGER:
case NUM_BIG_INTEGER:
double d = narrow_float(n);
if (d == 0.0)
throw new ArithmeticException(OP_EVALUATION_ZERO_DIVISOR);
return norm_float(narrow_float(m) / d);
case NUM_DOUBLE:
d = narrow_float(n);
if (d == 0.0)
throw new ArithmeticException(OP_EVALUATION_ZERO_DIVISOR);
return norm_float(narrow_float(m) / d);
default:
throw new UnsupportedOperationException(OP_PERMISSION_NOT_SUPPORTED);
}
}
/********************************************************************/
/* New Arithmetical Operations: */
/* ^/2: intPow() */
/********************************************************************/
/**
* <p>Power the two Prolog number.</p>
*
* @param m The base number.
* @param n The exponent number.
* @return The first integer raised to the power of the second integer.
*/
public static Number intPow(Number m, Number n) {
switch (Math.max(numType(m), numType(n))) {
case NUM_INTEGER:
int y = n.intValue();
if (y < 0)
throw new RangeException(OP_DOMAIN_NOT_LESS_THAN_ZERO, n);
int x = m.intValue();
if (x != Integer.MIN_VALUE && bitlength(Math.abs(x)) * y < 63) {
return norm_smallint(pow(x, y));
} else {
return norm_bigint(BigInteger.valueOf(x).pow(y));
}
case NUM_BIG_INTEGER:
if (n instanceof BigInteger)
throw new ArithmeticException("representation_error");
y = n.intValue();
if (y < 0)
throw new RangeException(OP_DOMAIN_NOT_LESS_THAN_ZERO, n);
return norm_bigint(((BigInteger) m).pow(y));
case NUM_DOUBLE:
return norm_float(Math.pow(narrow_float(m), narrow_float(n)));
default:
throw new UnsupportedOperationException(OP_PERMISSION_NOT_SUPPORTED);
}
}
/**
* <p>Compute the bitlength.</p>
*
* @param m The base, positive or negative.
* @return The bitlength.
*/
private static int bitlength(long m) {
return 64 - Long.numberOfLeadingZeros(m);
}
/**
* <p>Compute the power.</p>
*
* @param m The base, positive or negative.
* @param n The exponent, positive.
* @return The exponentiation.
*/
private static long pow(long m, int n) {
long r = 1;
while (n != 0) {
if ((n & 1) != 0)
r *= m;
n >>= 1;
if (n != 0)
m *= m;
}
return r;
}
/********************************************************************/
/* Rounding Operations (Part I): */
/* (//)/2: slashSlash() */
/* (rem)/2: rem() */
/* (div)/2: div() */
/* (mod)/2: mod() */
/********************************************************************/
/**
* <p>Divide and truncate the two numbers.</p>
* <p>The results corresponds to the truncation of the real division.</p>
* <pre>
* X // Y = truncate(X / Y).
* </pre>
*
* @param a The first operand.
* @param b The second operand.
* @return The first operand divided by the second operand.
*/
public static Number slashSlash(Number a, Number b) {
if (a instanceof Integer && b instanceof Integer) {
int u = b.intValue();
if (u == 0)
throw new ArithmeticException(OP_EVALUATION_ZERO_DIVISOR);
int v = a.intValue();
if (v == Integer.MIN_VALUE && u == -1) {
return ComputeElem.NEG_MIN_INTEGER;
} else {
return Integer.valueOf(v / u);
}
} else {
BigInteger p = ComputeElem.widen_bigint(b);
if (p.signum() == 0)
throw new ArithmeticException(OP_EVALUATION_ZERO_DIVISOR);
return ComputeElem.norm_bigint(ComputeElem.widen_bigint(a).divide(p));
}
}
/**
* <p>Remainder of the two numbers.</p>
* <p>The result is related to (//)/2 as follows:</p>
* <pre>
* X rem Y = X - (X // Y) * Y.
* </pre>
*
* @param a The first operand.
* @param b The second operand.
* @return The remainder of the first operand by the second operand.
* @throws ArithmeticException Illegal value.
*/
public static Number rem(Number a, Number b)
throws ArithmeticException {
if (a instanceof Integer && b instanceof Integer) {
int u = b.intValue();
if (u == 0)
throw new ArithmeticException(OP_EVALUATION_ZERO_DIVISOR);
return Integer.valueOf(a.intValue() % u);
} else {
BigInteger p = ComputeElem.widen_bigint(b);
if (p.signum() == 0)
throw new ArithmeticException(OP_EVALUATION_ZERO_DIVISOR);
return ComputeElem.norm_bigint(ComputeElem.widen_bigint(a).remainder(p));
}
}
/**
* <p>Divide and floor the two numbers.</p>
* <p>The results corresponds to the floor of the real division.</p>
* <pre>
* X div Y = floor(X / Y).
* </pre>
*
* @param a The first operand.
* @param b The second operand.
* @return The first operand divided by the second operand.
* @throws ArithmeticException Illegal value.
*/
public static Number div(Number a, Number b)
throws ArithmeticException {
if (a instanceof Integer && b instanceof Integer) {
int u = b.intValue();
if (u == 0)
throw new ArithmeticException(OP_EVALUATION_ZERO_DIVISOR);
int v = a.intValue();
if (v == Integer.MIN_VALUE && u == -1) {
return ComputeElem.NEG_MIN_INTEGER;
} else {
return Integer.valueOf(Math.floorDiv(v, u));
}
} else {
BigInteger p = ComputeElem.widen_bigint(b);
if (p.signum() == 0)
throw new ArithmeticException(OP_EVALUATION_ZERO_DIVISOR);
BigInteger val = div(ComputeElem.widen_bigint(a), p);
return ComputeElem.norm_bigint(val);
}
}
/**
* <p>Modulus of the two numbers.</p>
* <p>The result is related to (div)/2 as follows:</p>
* <pre>
* X mod Y = X - (X div Y) * Y.
* </pre>
*
* @param a The first number.
* @param b The second number.
* @return The remainder of the first number by the second number.
* @throws ArithmeticException Illegal value.
*/
public static Number mod(Number a, Number b)
throws ArithmeticException {
if (a instanceof Integer && b instanceof Integer) {
int u = b.intValue();
if (u == 0)
throw new ArithmeticException(OP_EVALUATION_ZERO_DIVISOR);
return Integer.valueOf(mod(a.intValue(), u));
} else {
BigInteger p = ComputeElem.widen_bigint(b);
if (p.signum() == 0)
throw new ArithmeticException(OP_EVALUATION_ZERO_DIVISOR);
BigInteger val = mod(ComputeElem.widen_bigint(a), p);
return ComputeElem.norm_bigint(val);
}
}
/*******************************************************************/
/* Integer Div Mod */
/*******************************************************************/
/**
* <p>Compute the div.</p>
*
* @param v The numerator.
* @param u The denumerator.
* @return The div.
*/
private static BigInteger div(BigInteger v, BigInteger u) {
BigInteger h;
if ((v.signum() < 0) != (u.signum() < 0)) {
BigInteger[] res = v.divideAndRemainder(u);
h = res[0];
if (res[1].signum() != 0)
h = h.subtract(BigInteger.ONE);
} else {
h = v.divide(u);
}
return h;
}
/**
* <p>Compute the mod.</p>
*
* @param v The numerator.
* @param u The denumerator.
* @return The mod.
*/
private static BigInteger mod(BigInteger v, BigInteger u) {
BigInteger res = v.remainder(u);
if ((v.signum() < 0) != (u.signum() < 0)) {
if (res.signum() != 0)
res = res.add(u);
}
return res;
}
/**
* <p>Compute the mod.</p>
*
* @param v The numerator.
* @param u The denumerator.
* @return The mod.
*/
public static int mod(int v, int u) {
int res = v % u;
if ((v < 0) != (u < 0)) {
if (res != 0)
res += u;
}
return res;
}
}