com.github.jessemull.microflexbiginteger.util.ImmutableMathUtil Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of microflex-biginteger Show documentation
Show all versions of microflex-biginteger Show documentation
Microplate library for parsing wet lab data.
The newest version!
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 Declaration ---------------------------*/
package com.github.jessemull.microflexbiginteger.util;
/*------------------------------- Dependencies -------------------------------*/
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;
/**
* Implementation of big integer and big decimal methods missing from the java
* math library but required for the MicroFlex math and statistics packages.
*
* The immutable math utility currently supports the following additional
* operations for the BigDecimal and BigInteger classes:
*
*
* Operation
*
* Square Root
*
*
* Fractional Exponentiation
*
*
* Natural Exponentiation
*
*
* Natural Log
*
*
*
* @author Jesse L. Mull
* @update Updated Oct 20, 2016
* @address http://www.jessemull.com
* @email [email protected]
*/
public class ImmutableMathUtil {
/*---------------- Methods for calculating the square root ---------------*/
/**
* Returns the square root of a big integer
* @param BigInteger randicand the input big integer randicand
* @return the result
*/
public static BigInteger sqrt(BigInteger randicand) {
BigInteger current = BigInteger.ZERO.setBit(randicand.bitLength()/2);
BigInteger next = current;
while(true){
BigInteger result = current.add(randicand.divide(current)).shiftRight(1);
if(result.equals(current) || result.equals(next)) {
return result;
}
next = current;
current = result;
}
}
/**
* Returns the square root of a BigDecimal.
* @param BigDecimal randicand the input BigDecimal randicand
* @return the result
*/
static public BigDecimal sqrt(BigDecimal randicand) {
MathContext context = new MathContext(randicand.precision());
BigDecimal two = new BigDecimal(2);
BigDecimal result = randicand.divide(two, context);
boolean finished = false;
int iterations = context.getPrecision() + 1;
int count = 0;
while(!finished && count < iterations) {
BigDecimal next = randicand.divide(result, context);
next = next.add(result);
next = next.divide(two, context);
finished = next.equals(result);
result = next;
iterations++;
}
return result;
}
/**
* Returns the square root of a BigDecimal.
* @param BigDecimal randicand the input BigDecimal randicand
* @param MathContextcontext the math context
* @return the result
*/
static public BigDecimal sqrt(BigDecimal randicand, MathContext context) {
BigDecimal two = new BigDecimal(2, context);
BigDecimal result = randicand.divide(two, context);
boolean finished = false;
int iterations = context.getPrecision() + 1;
int count = 0;
while(!finished && count < iterations) {
BigDecimal next = randicand.divide(result, context);
next = next.add(result);
next = next.divide(two, context);
finished = next.equals(result);
result = next;
iterations++;
}
return result;
}
/*-------------------- Methods for calculating powers --------------------*/
/**
* Raises the base to the power and returns the result. Calculates the power
* by finding the natural log using a Taylor sequence and exp using Newton's
* method of approximating the nth root.
* @param BigDecimal base the base
* @param BigDecimal power the power
* @return the result
*/
static public BigDecimal pow(BigDecimal base, BigDecimal power) {
int precisionBase = base.precision();
int precisionPower = power.precision();
int precision = precisionBase < precisionPower ? precisionBase : precisionPower;
MathContext context = new MathContext(precision, RoundingMode.HALF_DOWN);
return pow(base, power, context);
}
/**
* Raises the base to the power and returns the result. Calculates the power
* by finding the natural log using a Taylor sequence and exp using Newton's
* method of approximating the nth root.
* @param BigDecimal the base
* @param BigDecimal the power
* @param MathContext the math context
* @return the result
*/
static public BigDecimal pow(BigDecimal base, BigDecimal power, MathContext context) {
if(base.compareTo(BigDecimal.ZERO) < 0) {
throw new ArithmeticException("Base cannot be negative.");
}
if(base.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ZERO;
}
BigDecimal result = ln(base);
result = power.multiply(result, context);
result = exp(result, context);
return result;
}
/*-------------- Methods for calculating the natural log ---------------*/
/**
* Calculates the natural logarithm using a Taylor sequqnce.
* @param BigDecimal the input big decimal > 0
* @return the natural logarithm
*/
public static BigDecimal ln(BigDecimal input) {
int precision = input.precision();
MathContext context = new MathContext(precision, RoundingMode.HALF_DOWN);
return ln(input, context);
}
/**
* Calculates the natural logarithm using a Taylor sequqnce.
* @param BigDecimal the input big decimal > 0
* @return the natural logarithm
*/
public static BigDecimal ln(BigDecimal input, MathContext context) {
BigDecimal two = new BigDecimal("2", context);
BigDecimal inputMinus = input.subtract(BigDecimal.ONE, context);
BigDecimal inputPlus = input.add(BigDecimal.ONE, context);
BigDecimal y = inputMinus.divide(inputPlus, context);
BigDecimal result = new BigDecimal("0", context);
BigDecimal last = new BigDecimal(result.toString(), context);
int k = 0;
while(true) {
BigDecimal argumentOne = BigDecimal.ONE.divide(BigDecimal.ONE.add(two.multiply(new BigDecimal(k), context), context), context);
BigDecimal argumentTwo = y.pow(k * 2, context);
result = result.add(argumentOne.multiply(argumentTwo, context), context);
if(last.equals(result)) {
break;
}
last = new BigDecimal(result.toString(), context);
k++;
}
return y.multiply(two, context).multiply(result, context);
}
/*------------------ Methods for raising e to a power -------------------*/
/**
* Raises e to the power of the input big decimal.
* @param BigDecimal the input power
* @return the result
*/
public static BigDecimal exp(BigDecimal power) {
int precision = power.precision();
MathContext context = new MathContext(precision, RoundingMode.HALF_DOWN);
return exp(power, context);
}
/**
* Returns e raised to the input power using a Taylor expansion with
* @param BigDecimal the power
* @param MathContext the math context
* @return e raised to the input power
*/
public static BigDecimal exp(BigDecimal input, MathContext context) {
int taylorTerms = 8;
if(input.compareTo(BigDecimal.ZERO) < 0) {
BigDecimal inverse = exp(input.negate(), context);
return BigDecimal.ONE.divide(inverse, context);
} else if(input.compareTo(BigDecimal.ZERO) == 0) {
return BigDecimal.ONE.setScale(context.getPrecision(), RoundingMode.HALF_DOWN);
} else {
double inputDouble = input.doubleValue();
double inputUlpDouble = input.ulp().doubleValue();
double taylorNumberCheck1 = Math.pow(inputDouble, taylorTerms);
double taylorNumberCheck2 = taylorTerms * (taylorTerms - 1.0) * (taylorTerms - 2.0) * inputUlpDouble;
if(taylorNumberCheck1 < taylorNumberCheck2) {
BigDecimal result = BigDecimal.ONE;
BigDecimal inputPowerNum = BigDecimal.ONE;
BigInteger factorialNum = BigInteger.ONE;
int taylorPrecision = 1 + (int) (Math.log10(Math.abs(0.5 / (inputUlpDouble / taylorTerms))));
MathContext taylorContext = new MathContext(taylorPrecision);
for(int i = 1; i<= taylorTerms; i++) {
factorialNum = factorialNum.multiply(new BigInteger(i + ""));
inputPowerNum = inputPowerNum.multiply(input);
BigDecimal c = inputPowerNum.divide(new BigDecimal(factorialNum), taylorContext);
result = result.add(c);
double taylorNumberCheck3 = Math.abs(inputPowerNum.doubleValue());
double taylorNumberCheck4 = Math.abs(c.doubleValue());
double taylorNumberCheck5 = inputUlpDouble / 2;
if(taylorNumberCheck3 < i && taylorNumberCheck4 < taylorNumberCheck5) {
break;
}
}
int finalPrecision = 1 + (int) (Math.log10(Math.abs(0.5 / (inputUlpDouble / 2.0))));;
MathContext finalContext = new MathContext(finalPrecision);
return result.round(finalContext);
} else {
double taylorProduct = taylorTerms * (taylorTerms - 1.0) * (taylorTerms - 2.0) * inputUlpDouble;
double taylorPower = Math.pow(inputDouble, taylorTerms);
Double scaleBy10Double = 1.0 - Math.log10(taylorProduct / taylorPower) / (taylorTerms - 1.0);
int scaleBy10 = scaleBy10Double.intValue();
BigDecimal inputRaised10 = input.scaleByPowerOfTen(-scaleBy10);
BigDecimal expxby10 = exp(inputRaised10, context);
MathContext contextRaised10 = new MathContext(expxby10.precision() - scaleBy10);
while(scaleBy10 > 0) {
int minimum = Math.min(8, scaleBy10);
scaleBy10 -= minimum;
MathContext minContext = new MathContext(expxby10.precision() - minimum + 2);
int precisionMin = 1;
while (minimum-- > 0) {
precisionMin *= 10;
}
expxby10 = expxby10.pow(precisionMin, minContext);
}
return expxby10.round(contextRaised10);
}
}
}
}
© 2015 - 2024 Weber Informatics LLC | Privacy Policy