All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.xdev.jadoth.math.JaMath Maven / Gradle / Ivy

/*
 * XDEV Application Framework - XDEV Application Framework
 * Copyright © 2003 XDEV Software (https://xdev.software)
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program.  If not, see .
 */
package com.xdev.jadoth.math;

import java.awt.Point;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.Random;


/**
 * @author Thomas Muenz
 *
 */
public abstract class JaMath
{
	private static final Random random = new Random();
	
	///////////////////////////////////////////////////////////////////////////
	// Math Utils       //
	/////////////////////	
	
	/**
	 * This method is an int version of Math.pow(double, double), 
	 * using only integer iteration for calculation.
	 * 

* As a rule of thumb:
* It is faster for exponent < 250 (significantly faster for exponents < 100) * and slower for exponent >= 250 (significantly slower for exponents >= 500).
* This may depend on the concrete system running the program, of course. *
* Note that exponent may not be negative, otherwise an IllegalArgumentException is * thrown. * * (Why is there no Math.pow(int, int) in JDK ?) * * @param base * @param exponent my not be negative * @return base^exponent * @throws IllegalArgumentException if exponent is negative */ public static final int pow(final int base, int exponent) throws IllegalArgumentException { if(exponent < 0) throw new IllegalArgumentException("exponent may not be negative: "+exponent); if(exponent == 0) return 1; //return 1, even if base is 0! if(base == 0 || base == 1) return base; int result = 1; while(exponent --> 0){ result *= base; } return result; } public static final int pow2Bound(final int n) { int i = 1; while(i < n){ i <<= 1; } return i; } public static final int log2Bound(final int n) { int i = 1; int c = 0; while(i < n){ i <<= 1; c++; } return c; } public static final int log2pow2(final int pow2Value) { switch(pow2Value) { case 0: return 0; case 2: return 1; case 4: return 2; case 8: return 3; case 16: return 4; case 32: return 5; case 64: return 6; case 128: return 7; case 256: return 8; case 512: return 9; case 1024: return 10; case 2048: return 11; case 4096: return 12; case 8192: return 13; case 16384: return 14; case 32768: return 15; case 65536: return 16; case 131072: return 17; case 262144: return 18; case 524288: return 19; case 1048576: return 20; case 2097152: return 21; case 4194304: return 22; case 8388608: return 23; case 16777216: return 24; case 33554432: return 25; case 67108864: return 26; case 134217728: return 27; case 268435456: return 28; case 536870912: return 29; case 1073741824: return 30; default: throw new IllegalArgumentException("Not a power-of-2 value: "+pow2Value); } } /** * Determines if the passed value is a power-of-2 value. * @param value * @return true for any n in [1;30] that fulfills value = 2^n */ public static final boolean isPow2(final int value) { switch(value){ case 2: return true; case 4: return true; case 8: return true; case 16: return true; case 32: return true; case 64: return true; case 128: return true; case 256: return true; case 512: return true; case 1024: return true; case 2048: return true; case 4096: return true; case 8192: return true; case 16384: return true; case 32768: return true; case 65536: return true; case 131072: return true; case 262144: return true; case 524288: return true; case 1048576: return true; case 2097152: return true; case 4194304: return true; case 8388608: return true; case 16777216: return true; case 33554432: return true; case 67108864: return true; case 134217728: return true; case 268435456: return true; case 536870912: return true; case 1073741824: return true; default: return false; } } public static final float pow(final float base, int exponent) throws IllegalArgumentException { if(exponent < 0) throw new IllegalArgumentException("exponent may not be negative: "+exponent); if(exponent == 0) return 1; //return 1, even if base is 0! if(base == 0 || base == 1) return base; float result = 1; while(exponent --> 0){ result *= base; } return result; } public static final double pow(final double base, int exponent) throws IllegalArgumentException { if(exponent < 0) throw new IllegalArgumentException("exponent may not be negative: "+exponent); if(exponent == 0) return 1; //return 1, even if base is 0! if(base == 0 || base == 1) return base; double result = 1; while(exponent --> 0){ result *= base; } return result; } public static final float square(final float f) { return f*f; } public static final long square(final long l) { return l*l; } public static final int square(final int i) { return i*i; } public static final double square(final double d) { return d*d; } public static final float cube(final float f) { return f*f*f; } public static final long cube(final long l) { return l*l*l; } public static final int cube(final int i) { return i*i*i; } public static final double cube(final double d) { return d*d*d; } /** * Normalizes value to the actual closest value for decimals decimals.
* This is useful if multiple subsequent calulations with double values accumulate rounding errors that drift the * value away from the value it actually should (could) be.
* See the "candy" example in Joshua Bloch's "Effective Java": this method fixes the problem. *

* Note that decimals may not be negative.
* And note that while a value of 0 for decimals will yield the correct result, it makes not much * sense to call this method for it in the first place. * * @param value any double value * @param decimals the number of decimals. May not be negative. * @return the normalized value for value */ public static final double round(final double value, int decimals) { switch (decimals){ //common cases are hardcoded for performance reasons, inlined rounding code (Math.round(value*d)/d) case 0: return ((long)StrictMath.floor(value + 0.5d)); case 1: return ((long)StrictMath.floor(value*10.0d + 0.5d))/10.0d; case 2: return ((long)StrictMath.floor(value*100.0d + 0.5d))/100.0d; case 3: return ((long)StrictMath.floor(value*1000.0d + 0.5d))/1000.0d; case 4: return ((long)StrictMath.floor(value*10000.0d + 0.5d))/10000.0d; case 5: return ((long)StrictMath.floor(value*100000.0d + 0.5d))/100000.0d; case 6: return ((long)StrictMath.floor(value*1000000.0d + 0.5d))/1000000.0d; default:{ //generic algorithm, including range checks if(decimals < 0) { throw new IllegalArgumentException("No negative values allowed for decimals: "+decimals); } //No idea if 308 is right, tbh. At least it's a check for values like million etc. if(decimals > 308) { throw new IllegalArgumentException("Exponent out of range: "+decimals); } //inlined pow(double,int) without checks double factor = 1.0d; while(decimals --> 0){ factor *= 10.0d; } return ((long)StrictMath.floor(value*factor + 0.5d))/factor; } } } /** * Range. * * @param from the from * @param to the to * @return the range */ public static _intRange range(final int from, final int to){ return new _intRange(from, to); } public static byte[] sequence(byte from, final byte to) { byte[] range; if(from < to){ range = new byte[to-from + 1]; for(int i = 0; i < range.length; i++) { range[i] = from++; } } else { range = new byte[from-to + 1]; for(int i = 0; i < range.length; i++) { range[i] = from--; } } return range; } public static short[] sequence(short from, final short to) { short[] range; if(from < to){ range = new short[to-from + 1]; for(int i = 0; i < range.length; i++) { range[i] = from++; } } else { range = new short[from-to + 1]; for(int i = 0; i < range.length; i++) { range[i] = from--; } } return range; } /** * Sequence. * * @param from the from * @param to the to * @return the int[] */ public static int[] sequence(int from, final int to) { int[] range; if(from < to){ range = new int[to-from + 1]; for(int i = 0; i < range.length; i++) { range[i] = from++; } } else { range = new int[from-to + 1]; for(int i = 0; i < range.length; i++) { range[i] = from--; } } return range; } /** * * @param from * @param to * @return * @throws IllegalArgumentException if the range [from;to] is greater than Integer.MAX_VALUE */ public static long[] sequence(long from, final long to) throws IllegalArgumentException { long[] range; if(from < to){ final long elementCount = to-from+1; if(elementCount > Integer.MAX_VALUE){ throw new IllegalArgumentException( "Range ["+from+";"+to+"] exceeds array range limit: "+elementCount+" > "+Integer.MAX_VALUE ); } range = new long[(int)elementCount]; for(int i = 0; i < range.length; i++) { range[i] = from++; } } else { final long elementCount = from-to+1; if(elementCount > Integer.MAX_VALUE){ throw new IllegalArgumentException( "Range ["+from+";"+to+"] exceeds array range limit: "+elementCount+" > "+Integer.MAX_VALUE ); } range = new long[(int)elementCount]; for(int i = 0; i < range.length; i++) { range[i] = from--; } } return range; } public static final double max(final double... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); double currentMax = -Double.MIN_VALUE; for(final double d : values) { if(d > currentMax) currentMax = d; } return currentMax; } public static final float max(final float... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); float currentMax = -Float.MIN_VALUE; for(final float f : values) { if(f > currentMax) currentMax = f; } return currentMax; } public static final int max(final int... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); int currentMax = Integer.MIN_VALUE; for(final int i : values) { if(i > currentMax) currentMax = i; } return currentMax; } public static final long max(final long... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); long currentMax = Long.MIN_VALUE; for(final long l : values) { if(l > currentMax) currentMax = l; } return currentMax; } public static final double min(final double... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); double currentMin = -Double.MAX_VALUE; for(final double d : values) { if(d < currentMin) currentMin = d; } return currentMin; } public static final float min(final float... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); float currentMin = -Float.MAX_VALUE; for(final float f : values) { if(f < currentMin) currentMin = f; } return currentMin; } public static final int min(final int... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); int currentMin = Integer.MAX_VALUE; for(final int i : values) { if(i < currentMin) currentMin = i; } return currentMin; } public static final long min(final long... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); long currentMin = Long.MAX_VALUE; for(final long l : values) { if(l < currentMin) currentMin = l; } return currentMin; } public static final double sum(final double... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); double sum = 0d; for(final double d : values) { sum += d; } return sum; } public static final float sum(final float... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); float sum = 0f; for(final float f : values) { sum += f; } return sum; } public static final int sum(final int... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); int sum = 0; for(final int i : values) { sum += i; } return sum; } public static final long sum(final long... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); long sum = 0; for(final long l : values) { sum += l; } return sum; } public static final double avg(final double... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); double sum = 0d; for(final double d : values) { sum += d; } return sum/values.length; } public static final float avg(final float... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); float sum = 0f; for(final float f : values) { sum += f; } return sum/values.length; } public static final int avg(final int... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); int sum = 0; for(final int i : values) { sum += i; } return sum/values.length; } public static final long avg(final long... values) { if(values == null) throw new IllegalArgumentException("values may not be null"); if(values.length == 0) throw new IllegalArgumentException("values may not be empty"); long sum = 0; for(final long l : values) { sum += l; } return sum/values.length; } /////////////////////////////////////////////////////////////////////////// // Bresenham // ///////////////////// /** * Determines the amount of discrete steps from (x1,y1) to (x2,y2), where one step is a change of coordinates * in either straight or diagonal direction.

* Examples:
* (0,0) to (2,0) = 2 steps
* (0,0) to (2,2) = 2 steps
* (5,18) to (10,9) = 9 steps
* * @param x1 * @param y1 * @param x2 * @param y2 * @return */ public static final int stepCountDistance(final int x1, final int y1, final int x2, final int y2) { return Math.max(Math.abs(x1-x2), Math.abs(y1-y2)); } public static final Point[] linePoints(final int x1, final int y1, final int x2, final int y2) { int x = x1, y = y1, d = 0, hx = x2-x1, hy = y2-y1, c, m, xInc = 1, yInc = 1; final Point[] points = new Point[Math.max(Math.abs(x1-x2), Math.abs(y1-y2))+1]; int idx = 0; if(hx < 0) { xInc = -1; hx = -hx; } if(hy < 0) { yInc = -1; hy = -hy; } if(hy <= hx) { c = 2 * hx; m = 2 * hy; while(true){ points[idx++] = new Point(x, y); if(x == x2) break; x += xInc; d += m; if(d > hx) { y += yInc; d -= c; } } } else { c = 2 * hy; m = 2 * hx; while(true){ points[idx++] = new Point(x, y); if(y == y2) break; y += yInc; d += m; if(d > hy) { x += xInc; d -= c; } } } return points; } public static final int[] linePointsInt1D(final int x1, final int y1, final int x2, final int y2) { int x = x1, y = y1, d = 0, hx = x2-x1, hy = y2-y1, c, m, xInc = 1, yInc = 1; final int[] points = new int[(Math.max(Math.abs(x1-x2), Math.abs(y1-y2))+1)*2]; int idx = 0; if(hx < 0) { xInc = -1; hx = -hx; } if(hy < 0) { yInc = -1; hy = -hy; } if(hy <= hx) { c = 2 * hx; m = 2 * hy; while(true){ points[idx++] = x; points[idx++] = y; if(x == x2) break; x += xInc; d += m; if(d > hx) { y += yInc; d -= c; } } } else { c = 2 * hy; m = 2 * hx; while(true){ points[idx++] = x; points[idx++] = y; if(y == y2) break; y += yInc; d += m; if(d > hy) { x += xInc; d -= c; } } } return points; } public static final int[][] linePointsInt2D(final int x1, final int y1, final int x2, final int y2) { int x = x1, y = y1, d = 0, hx = x2-x1, hy = y2-y1, c, m, xInc = 1, yInc = 1; final int[][] points = new int[Math.max(Math.abs(x1-x2), Math.abs(y1-y2))+1][]; int idx = 0; if(hx < 0) { xInc = -1; hx = -hx; } if(hy < 0) { yInc = -1; hy = -hy; } if(hy <= hx) { c = 2 * hx; m = 2 * hy; while(true){ points[idx++] = new int[]{x, y}; if(x == x2) break; x += xInc; d += m; if(d > hx) { y += yInc; d -= c; } } } else { c = 2 * hy; m = 2 * hx; while(true){ points[idx++] = new int[]{x, y}; if(y == y2) break; y += yInc; d += m; if(d > hy) { x += xInc; d -= c; } } } return points; } public static final void line( final int x1, final int y1, final int x2, final int y2, final IntCoordinateManipulator manipulator ) throws InvalidCoordinateException { int x = x1, y = y1, d = 0, hx = x2-x1, hy = y2-y1, c, m, xInc = 1, yInc = 1; if(hx < 0) { xInc = -1; hx = -hx; } if(hy < 0) { yInc = -1; hy = -hy; } if(hy <= hx) { c = 2 * hx; m = 2 * hy; while(true){ manipulator.manipulateCoordinate(x, y); if(x == x2) break; x += xInc; d += m; if(d > hx) { y += yInc; d -= c; } } } else { c = 2 * hy; m = 2 * hx; while(true){ manipulator.manipulateCoordinate(x, y); if(y == y2) break; y += yInc; d += m; if(d > hy) { x += xInc; d -= c; } } } } /** * Use factorial(long) for n in [0;20].
* Use factorial(BigInteger) for any n > 0. * @param n natural number in [0;12] * @return n! * @throws IllegalArgumentException for n < 0 or n > 12 */ public static final int factorial(final int n) throws IllegalArgumentException { //honestly: calculate the loop everytime for a value set of only 13 elements? switch(n){ case 12: return 479001600; case 11: return 39916800; case 10: return 3628800; case 9: return 362880; case 8: return 40320; case 7: return 5040; case 6: return 720; case 5: return 120; case 4: return 24; case 3: return 6; case 2: return 2; case 1: return 1; case 0: return 1; default: throw new IllegalArgumentException("n not in [0;12]: "+n); } //calculation // if(n < 0) throw new IllegalArgumentException("n may not be negative: "+n); // if(n > 12) throw new IllegalArgumentException("n may not be greater 12: "+n); // int result = 1; // while(n > 1) result *= n--; // return result; } /** * Use factorial(BigInteger) for any n > 0. * * @param n natural number in [0;20] * @return n! * @throws IllegalArgumentException for n < 0 or n > 20 */ public static final long factorial(final long n) throws IllegalArgumentException { //honestly: calculate the loop everytime for a value set of only 21 elements? switch((int)n){ case 20: return 2432902008176640000L; case 19: return 121645100408832000L; case 18: return 6402373705728000L; case 17: return 355687428096000L; case 16: return 20922789888000L; case 15: return 1307674368000L; case 14: return 87178291200L; case 13: return 6227020800L; case 12: return 479001600L; case 11: return 39916800L; case 10: return 3628800L; case 9: return 362880L; case 8: return 40320L; case 7: return 5040L; case 6: return 720L; case 5: return 120L; case 4: return 24L; case 3: return 6L; case 2: return 2L; case 1: return 1L; case 0: return 1L; default: throw new IllegalArgumentException("n not in [0;20]: "+n); } //calculation // if(n < 0) throw new IllegalArgumentException("n may not be negative: "+n); // if(n > 20) throw new IllegalArgumentException("n may not be greater 20: "+n); // int i = (int)n; // long result = 1; // while(i > 1) result *= i--; // return result; } /** * * @param n any natural number >= 0 * @return n! * @throws IllegalArgumentException for n < 0 */ public static final BigInteger factorial(BigInteger n) throws IllegalArgumentException { //recursive algorithms are nonsense here as the doy method call and range checking overhead in every step! final long nValue = n.longValue(); if(nValue < 0) throw new IllegalArgumentException("n may not be negative: "+nValue); if(nValue <= 20) return BigInteger.valueOf(factorial(nValue)); BigInteger result = BigInteger.valueOf(factorial(20L)); while(n.longValue() > 20){ result = result.multiply(n); n = n.subtract(BigInteger.ONE); } return result; } /** * Alias for BigInteger.valueOf(value) * @param value any value * @return a BigInteger representing value */ public static final BigInteger bigInt(final int value) { return BigInteger.valueOf(value); } /** * Alias for BigInteger.valueOf(value) * @param value any value * @return a BigInteger representing value */ public static final BigInteger bigInt(final long value) { return BigInteger.valueOf(value); } /** * Alias for BigDecimal.valueOf(value) * @param value any value * @return a BigDecimal representing value */ public static final BigDecimal bigDec(final long value) { return BigDecimal.valueOf(value); } /** * Alias for BigDecimal.valueOf(value) * @param value any value * @return a BigDecimal representing value */ public static final BigDecimal bigDec(final double value) { return BigDecimal.valueOf(value); } /////////////////////////////////////////////////////////////////////////// // constructors // ///////////////////// private JaMath(){} /** * @return the random */ public static final Random random() { return random; } public static final int random(final int n) { return random.nextInt(n); } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy