/*
* Copyright (C) 2011-2014 GeoForge Project
*
* 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 <http://www.gnu.org/licenses/>.
*/
package org.geoforge.java.math;
import java.math.BigDecimal;
import java.math.BigInteger;
/**
* @author Amadeus Sowerby
* amadeus.Sowerby@gmail.com
*
*
*/
public class GfrBigMath
{
private static BigDecimal _BDM_ERROR_;
private static int _INT_ITERATIONS_;
private static int _INT_MAX_ITERATIONS_ = 200;
private static int _INT_PRECISON_ = 100;//number of decimals
public final static BigDecimal NMB__1 = new BigDecimal("-1");
public final static BigDecimal NMB_0 = new BigDecimal("0");
public final static BigDecimal NMB_1 = new BigDecimal("1");
public final static BigDecimal NMB_2 = new BigDecimal("2");
public final static BigDecimal NMB_3 = new BigDecimal("3");
public final static BigDecimal NMB_4 = new BigDecimal("4");
public final static BigDecimal NMB_60 = new BigDecimal("60");
public final static BigDecimal NMB_90 = new BigDecimal("90");
public final static BigDecimal NMB_180 = new BigDecimal("180");
public final static BigDecimal NMB_3600 = new BigDecimal("3600");
public final static BigDecimal NMB_10_14 = new BigDecimal("10000000000000");
public final static BigDecimal NMB_PI = new BigDecimal("3.141592653589793238462643383279502884197169399375105820974944592307816406286208998628034825342117067982148086");
private static int _INT_ROUNDING_MODE_ = BigDecimal.ROUND_DOWN;
public static BigDecimal ddecToDms(
int deg,
int min,
double sec,
int scale)
{
BigDecimal bdlDeg = new BigDecimal(deg);
BigDecimal bdlMin = new BigDecimal(min);
BigDecimal bdlSec = new BigDecimal(sec);
return bdlDeg.add(bdlMin.divide(NMB_60, scale, GfrBigMath._INT_ROUNDING_MODE_)).add(bdlSec.divide(NMB_3600, scale, java.math.RoundingMode.FLOOR));
}
public static BigDecimal degToRad(BigDecimal degAngle, int scale)
{
return degAngle.divide(GfrBigMath.NMB_180, scale, _INT_ROUNDING_MODE_).multiply(GfrBigMath.pi(scale));
}
public static BigDecimal radToDeg(BigDecimal degAngle, int scale)
{
return degAngle.divide(GfrBigMath.pi(scale), scale, _INT_ROUNDING_MODE_).multiply(GfrBigMath.NMB_180);
}
public static BigDecimal tan(BigDecimal angle, int scale)
{
return sin(angle, scale).divide(cos(angle, scale), scale, _INT_ROUNDING_MODE_);
}
public static BigDecimal sin(BigDecimal angle, int scale)
{
double dblAngle = Double.parseDouble(angle.toString());
double zero = 0;
double pi_2 = Double.parseDouble(GfrBigMath.divide(NMB_PI, NMB_2).toString());
angle.subtract(GfrBigMath.divide(NMB_PI, NMB_2));
if ( Math.abs(dblAngle - zero) < 0.0000000001D)
{
return NMB_0;
}
if ( Math.abs(dblAngle - pi_2) < 0.0000000001D)
{
return NMB_1;
}
BigDecimal r = NMB_0;
BigDecimal r__1 = NMB_0;
int i = 0;
BigDecimal p = NMB__1;
p = p.multiply(NMB__1);
_INT_ITERATIONS_ = 0;
boolean more = true;
while (more)
{
r__1 = r;
int i_x2_p1 = 2 * i + 1;
r = r.add(
(p.multiply(angle.pow(i_x2_p1))).divide(fact(i_x2_p1), scale, java.math.RoundingMode.FLOOR));
p = p.multiply(NMB__1);
i++;
if (++_INT_ITERATIONS_ >= _INT_MAX_ITERATIONS_)
{
System.out.println("Iter : " + i);
more = false;
}
else if (r__1.equals(r))
{
more = false;
}
}
return r;
}
public static BigDecimal cos(BigDecimal angle, int scale)
{
angle.subtract(GfrBigMath.divide(NMB_PI, NMB_2));
if ( (GfrBigMath.divide(NMB_PI, NMB_2)).compareTo(angle) == 0)
{
return NMB_0;
}
if ( (NMB_0).compareTo(angle) == 0)
{
return NMB_1;
}
BigDecimal r = NMB_0;
BigDecimal r__1 = NMB_0;
int i = 0;
BigDecimal p = NMB__1;
p = p.multiply(NMB__1);
_INT_ITERATIONS_ = 0;
boolean more = true;
while (more)
{
r__1 = r;
int i_x2 = 2 * i;
r = r.add(
(p.multiply(angle.pow(i_x2))).divide(fact(i_x2), scale, _INT_ROUNDING_MODE_));
p = p.multiply(NMB__1);
i++;
if (++_INT_ITERATIONS_ >= _INT_MAX_ITERATIONS_)
{
System.out.println("Iter : " + i);
more = false;
}
else if (r__1.equals(r))
{
more = false;
}
}
return r;
}
public static BigDecimal fact(int nmb)
{
if (nmb == 0)
{
return NMB_1;
}
int n = nmb;
BigDecimal res = new BigDecimal(nmb);
BigDecimal bdlN;
while (n > 1)
{
n--;
bdlN = new BigDecimal(n);
res = res.multiply(bdlN);
}
return res;
}
private static BigDecimal getInitialApproximation(BigDecimal n)
{
BigInteger integerPart = n.toBigInteger();
int length = integerPart.toString().length();
if ((length % 2) == 0)
{
length--;
}
length /= 2;
BigDecimal guess = NMB_1.movePointRight(length);
return guess;
}
static public BigDecimal sqrt(BigDecimal n, int scale)
{
// Make sure n is a positive number
if (n.compareTo(NMB_0) <= 0)
{
throw new IllegalArgumentException();
}
BigDecimal initialGuess = getInitialApproximation(n);
BigDecimal lastGuess = NMB_0;
BigDecimal guess = new BigDecimal(initialGuess.toString());
// Iterate
_INT_ITERATIONS_ = 0;
boolean more = true;
while (more)
{
lastGuess = guess;
guess = n.divide(guess, scale, _INT_ROUNDING_MODE_);
guess = guess.add(lastGuess);
guess = guess.divide(NMB_2, scale, _INT_ROUNDING_MODE_);
_BDM_ERROR_ = n.subtract(guess.multiply(guess));
if (++_INT_ITERATIONS_ >= _INT_MAX_ITERATIONS_)
{
System.out.println("Iter : " + _INT_ITERATIONS_ );
more = false;
}
else if (lastGuess.equals(guess))
{
more = _BDM_ERROR_.abs().compareTo(NMB_1) >= 0;
}
}
return guess;
}
static public BigDecimal log(BigDecimal n, int scale)
{
GfrBigMath.setScale(scale);
BigDecimal x = n;
BigDecimal r = NMB_0;
r.setScale(scale);
BigDecimal r__1 = NMB_0;
int i = 0;
// Iterate
boolean more = true;
while (more)
{
r__1 = r;
int i_x2_p1 = 2 * i + 1;
BigDecimal dblI_x2_p1 = new BigDecimal(i_x2_p1);
//r.setScale(scale, java.math.RoundingMode.FLOOR);
r = r.add(
GfrBigMath.divide(
GfrBigMath.NMB_2,
dblI_x2_p1)
.multiply(
(GfrBigMath.divide(
x.subtract(GfrBigMath.NMB_1),
x.add(GfrBigMath.NMB_1))
).pow(i_x2_p1)));
r = GfrBigMath.divide(r, GfrBigMath.NMB_1);
if (i++ >= _INT_MAX_ITERATIONS_)
{
System.out.println("Iter : " + i );
more = false;
}
else if (r__1.equals(r))
{
more = false;
}
}
r.setScale(scale, _INT_ROUNDING_MODE_);
return r;
}
static public BigDecimal exp(BigDecimal n, int scale)
{
BigDecimal r = NMB_0;
BigDecimal r__1 = NMB_0;
int i = 0;
// Iterate
boolean more = true;
while (more)
{
r__1 = r;
r = r.add(GfrBigMath.divide(n.pow(i), fact(i)));
if (i++ >= _INT_MAX_ITERATIONS_)
{
System.out.println("Iter : " + i);
more = false;
}
else if (r__1.equals(r))
{
more = false;
}
}
return r;
}
static public BigDecimal pow(BigDecimal a, BigDecimal b, int scale)
{
return exp(b.multiply(log(a, scale)), scale);
}
static public BigDecimal atan(BigDecimal n, int scale)
{
BigDecimal r = NMB_0;
BigDecimal r__1 = NMB_0;
int i = 0;
BigDecimal p = NMB__1;
p = p.multiply(NMB__1);
// Iterate
boolean more = true;
while (more)
{
int i_x2_p1 = 2 * i + 1;
BigDecimal bdlI_x2_p1 = new BigDecimal(i_x2_p1);
r__1 = r;
r = r.add(p.multiply(GfrBigMath.divide(n.pow(i_x2_p1), bdlI_x2_p1)));
p = p.multiply(NMB__1);
if (i++ >= _INT_MAX_ITERATIONS_)
{
System.out.println("Iter : " + i);
more = false;
}
else if (r__1.equals(r))
{
more = false;
}
}
return r;
}
public static BigDecimal pi(int scale)
{
BigDecimal pi = NMB_PI;
pi.setScale(scale, BigDecimal.ROUND_HALF_UP);
return pi;
}
public static BigDecimal divide(BigDecimal num, BigDecimal den)
{
return num.divide(den, _INT_PRECISON_, _INT_ROUNDING_MODE_);
}
public static void setScale(int scale)
{
_INT_PRECISON_ = scale;
}
}