summaryrefslogtreecommitdiffstats
path: root/logo/src/xlogo/kernel/MyCalculator.java
diff options
context:
space:
mode:
authorMarko Živković <[email protected]>2014-06-11 10:10:33 +0000
committerMarko Živković <[email protected]>2014-06-11 10:10:33 +0000
commit1107aa0763e3d7554408c401d2a1dbed11a94c51 (patch)
tree7074264bc7b63f2ee5ee14a39458380fcce1904b /logo/src/xlogo/kernel/MyCalculator.java
Add initial directories and files
git-svn-id: https://svn.code.sf.net/p/xlogo4schools/svn/trunk@1 3b0d7934-f7ef-4143-9606-b51f2e2281fd
Diffstat (limited to 'logo/src/xlogo/kernel/MyCalculator.java')
-rw-r--r--logo/src/xlogo/kernel/MyCalculator.java1221
1 files changed, 1221 insertions, 0 deletions
diff --git a/logo/src/xlogo/kernel/MyCalculator.java b/logo/src/xlogo/kernel/MyCalculator.java
new file mode 100644
index 0000000..1fde09c
--- /dev/null
+++ b/logo/src/xlogo/kernel/MyCalculator.java
@@ -0,0 +1,1221 @@
+/* XLogo4Schools - A Logo Interpreter specialized for use in schools, based on XLogo by Lo�c Le Coq
+ * Copyright (C) 2013 Marko Zivkovic
+ *
+ * Contact Information: marko88zivkovic at gmail dot com
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the Free
+ * Software Foundation; either version 2 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 General
+ * Public License for more details. You should have received a copy of the
+ * GNU General Public License along with this program; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
+ * MA 02110-1301, USA.
+ *
+ *
+ * This Java source code belongs to XLogo4Schools, written by Marko Zivkovic
+ * during his Bachelor thesis at the computer science department of ETH Z�rich,
+ * in the year 2013 and/or during future work.
+ *
+ * It is a reengineered version of XLogo written by Lo�c Le Coq, published
+ * under the GPL License at http://xlogo.tuxfamily.org/
+ *
+ * Contents of this file were initially written by Lo�c Le Coq,
+ * modifications, extensions, refactorings might have been applied by Marko Zivkovic
+ */
+
+package xlogo.kernel;
+
+import java.math.MathContext;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.text.DecimalFormat;
+import java.text.DecimalFormatSymbols;
+import java.util.Stack;
+
+import xlogo.Logo;
+import xlogo.utils.Utils;
+
+public class MyCalculator
+{
+ private final BigDecimal tenth = new BigDecimal(0.1);
+
+ private MathContext mc = null;
+
+ // If precision is lesser than 16 (operation for double)
+ /**
+ * @uml.property name="lowPrecision"
+ */
+ private boolean lowPrecision = true;
+
+ /**
+ * Indicates if the log table have been created
+ *
+ * @uml.property name="initLogTable"
+ */
+ private boolean initLogTable;
+ /**
+ * This is a table containing all BigDecimal ln(1+10^ (-k) ), k in
+ * {0,1....,digits-1} This are constants for the Cordic method to calculate
+ * ln, exp
+ *
+ * @uml.property name="logTable" multiplicity="(0 -1)" dimension="1"
+ */
+ private BigDecimal[] logTable;
+ /**
+ * Indicates if the trigonometric table have been created
+ *
+ * @uml.property name="initCosTable"
+ */
+ private boolean initCosTable;
+ /**
+ * This is a table containing all BigDecimal arctan 10^ (-k) , k in
+ * {0,1....,digits-1} This are constants for the Cordic method to calculate
+ * trigonometric functions
+ *
+ * @uml.property name="cosTable" multiplicity="(0 -1)" dimension="1"
+ */
+ private BigDecimal[] cosTable;
+
+ private static int digits;
+
+ protected MyCalculator(int digits)
+ {
+ MyCalculator.digits = digits;
+ initLogTable = false;
+ initCosTable = false;
+ if (digits < 16)
+ {
+ mc = new MathContext(16);
+ lowPrecision = true;
+ logTable = new BigDecimal[16];
+ cosTable = new BigDecimal[16];
+ }
+ else
+ {
+ mc = new MathContext(digits);
+ lowPrecision = false;
+ logTable = new BigDecimal[digits];
+ cosTable = new BigDecimal[digits];
+ }
+ }
+
+ /**
+ * Return The exponential of s according to matContext Precision
+ *
+ * @param s
+ * The number
+ * @return Exp(s)
+ * @throws LogoError
+ * if s isn't a number
+ */
+
+ protected String exp(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double nombre = numberDouble(s);
+ return teste_fin_double(Math.exp(nombre));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return expBD(bd).toPlainString();
+ }
+ }
+
+ /**
+ * Return The logarithm of s according to matContext Precision
+ *
+ * @param s
+ * The number
+ * @return log(s)
+ * @throws LogoError
+ * if s isn't a number or negative
+ */
+
+ protected String log(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double nombre = numberDouble(s);
+ if (nombre < 0 || nombre == 0)
+ {
+ String log = Utils.primitiveName("arithmetic.log");
+ throw new LogoError(log + " " + Logo.messages.getString("attend_positif"));
+ }
+ return teste_fin_double(Math.log(nombre));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ if (bd.signum() != 1)
+ {
+ String log = Utils.primitiveName("arithmetic.log");
+ throw new LogoError(log + " " + Logo.messages.getString("attend_positif"));
+ }
+ return logBD(bd).toPlainString();
+ }
+ }
+
+ /**
+ * Return The square root of s according to matContext Precision
+ *
+ * @param s
+ * The number
+ * @return sqrt(s)
+ * @throws LogoError
+ * if s isn't a number or negative
+ */
+
+ protected String sqrt(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double number = numberDouble(s);
+ if (number < 0)
+ {
+ String sqrt = Utils.primitiveName("arithmetic.racine");
+ throw new LogoError(sqrt + " " + Logo.messages.getString("attend_positif"));
+ }
+ return teste_fin_double(Math.sqrt(number));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ if (bd.signum() == -1)
+ {
+ String sqrt = Utils.primitiveName("arithmetic.racine");
+ throw new LogoError(sqrt + " " + Logo.messages.getString("attend_positif"));
+ }
+ return sqrtBD(bd).toPlainString();
+ }
+ }
+
+ /**
+ * Return the product of all elements in stack param
+ *
+ * @param param
+ * The stack of operands
+ * @return The product
+ * @throws LogoError
+ */
+ protected String multiply(Stack<String> param) throws LogoError
+ {
+ int size = param.size();
+ BigDecimal product = BigDecimal.ONE;
+ BigDecimal a;
+ for (int i = 0; i < size; i++)
+ {
+ a = numberDecimal(param.get(i));
+ product = product.multiply(a, mc);
+ }
+ return product.stripTrailingZeros().toPlainString();
+ }
+
+ protected String divide(Stack<String> param) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double a = numberDouble(param.get(0));
+ double b = numberDouble(param.get(1));
+ if (b == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return (teste_fin_double(a / b));
+ }
+ else
+ {
+ BigDecimal a = new BigDecimal(param.get(0), mc);
+ BigDecimal b = new BigDecimal(param.get(1), mc);
+ if (b.signum() == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return (a.divide(b, mc).stripTrailingZeros().toPlainString());
+ }
+ }
+
+ /**
+ * Return the sum of all elements in stack param
+ *
+ * @param param
+ * The stack of operands
+ * @return The sum
+ * @throws LogoError
+ */
+ protected String add(Stack<String> param) throws LogoError
+ {
+ int size = param.size();
+ BigDecimal sum = BigDecimal.ZERO;
+ BigDecimal a;
+ for (int i = 0; i < size; i++)
+ {
+ a = numberDecimal(param.get(i));
+ sum = sum.add(a, mc);
+ }
+
+ return sum.stripTrailingZeros().toPlainString();
+ }
+
+ protected String inf(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) < 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String sup(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) > 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String infequal(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) <= 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String supequal(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) >= 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String equal(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ if (a.compareTo(b) == 0)
+ return Logo.messages.getString("vrai");
+ return Logo.messages.getString("faux");
+ }
+
+ protected String substract(Stack<String> param) throws LogoError
+ {
+ BigDecimal a = numberDecimal(param.get(0));
+ BigDecimal b = numberDecimal(param.get(1));
+ return a.subtract(b, mc).stripTrailingZeros().toPlainString();
+ }
+
+ /**
+ * Returns the opposite of s
+ *
+ * @param s
+ * @return
+ */
+ protected String minus(String s) throws LogoError
+ {
+ BigDecimal a = numberDecimal(s);
+ return a.negate(mc).stripTrailingZeros().toPlainString();
+ }
+
+ protected String remainder(String a, String b) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ int aa = getInteger(a);
+ int bb = getInteger(b);
+ if (bb == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return teste_fin_double(aa % bb);
+ }
+ else
+ {
+ BigDecimal aa = getBigInteger(a);
+ BigDecimal bb = getBigInteger(b);
+ if (bb.signum() == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return aa.remainder(bb, mc).stripTrailingZeros().toPlainString();
+
+ }
+ }
+
+ protected String modulo(String a, String b) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ int aa = getInteger(a);
+ int bb = getInteger(b);
+ if (bb == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ double rem = aa % bb;
+ if (aa * bb < 0 && rem != 0)
+ rem = rem + bb;
+ return teste_fin_double(rem);
+ }
+ else
+ {
+ BigDecimal aa = getBigInteger(a);
+ BigDecimal bb = getBigInteger(b);
+ if (bb.signum() == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ BigDecimal rem = aa.remainder(bb, mc);
+ if (aa.multiply(bb).compareTo(BigDecimal.ZERO) == -1 && (!rem.equals(BigDecimal.ZERO)))
+ rem = rem.add(bb);
+ return rem.stripTrailingZeros().toPlainString();
+
+ }
+ }
+
+ protected String quotient(String a, String b) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double aa = numberDouble(a);
+ double bb = numberDouble(b);
+ if (bb == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return String.valueOf((int) (aa / bb));
+ }
+ else
+ {
+ BigDecimal aa = numberDecimal(a);
+ BigDecimal bb = numberDecimal(b);
+ if (bb.signum() == 0)
+ throw new LogoError(Logo.messages.getString("division_par_zero"));
+ return aa.divideToIntegralValue(bb, mc).stripTrailingZeros().toPlainString();
+
+ }
+ }
+
+ protected String truncate(String a) throws LogoError
+ {
+ BigDecimal ent = numberDecimal(a);
+ return ent.toBigInteger().toString();
+ }
+
+ protected String abs(String a) throws LogoError
+ {
+ BigDecimal e = numberDecimal(a);
+ return e.abs().stripTrailingZeros().toPlainString();
+ }
+
+ protected String power(String a, String b) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ double p = Math.pow(numberDouble(a), numberDouble(b));
+ // Bug pr power -1 0.5
+ Double p1 = new Double(p);
+ if (p1.equals(Double.NaN))
+ throw new LogoError(Utils.primitiveName("arithmetic.puissance") + " "
+ + Logo.messages.getString("attend_positif"));
+ // End Bug
+ return teste_fin_double(p);
+ }
+ else
+ {
+ // if the exposant is an integer
+ try
+ {
+ int n = Integer.parseInt(b);
+ BigDecimal aa = numberDecimal(a);
+ return aa.pow(n, mc).toPlainString();
+ }
+ catch (NumberFormatException e)
+ {
+ BigDecimal aa = numberDecimal(a);
+ BigDecimal bb = numberDecimal(b);
+ if (aa.signum() == 1)
+ {
+ return expBD(bb.multiply(logBD(aa), mc)).toPlainString();
+ }
+ else if (aa.signum() == 0)
+ return "0";
+ else
+ return String.valueOf(getInteger(b));
+ }
+ }
+ }
+
+ protected String log10(String s) throws LogoError
+ {
+ Stack<String> tmp = new Stack<String>();
+ tmp.push(log(s));
+ tmp.push(log("10"));
+ return divide(tmp);
+ }
+
+ protected String pi()
+ {
+ if (lowPrecision)
+ {
+ return String.valueOf(Math.PI);
+ }
+ else
+ {
+ return piBD().toPlainString();
+ }
+ }
+
+ protected String sin(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.sin(Math.toRadians(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return sinBD(bd).toPlainString();
+
+ }
+ }
+
+ protected String cos(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.cos(Math.toRadians(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return cosBD(bd).toPlainString();
+
+ }
+ }
+
+ protected String tan(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.tan(Math.toRadians(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return tanBD(bd).toPlainString();
+ }
+ }
+
+ protected String atan(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.toDegrees(Math.atan(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return toDegree(atanBD(bd)).toPlainString();
+ }
+ }
+
+ protected String acos(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.toDegrees(Math.acos(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return toDegree(acosBD(bd)).toPlainString();
+ }
+ }
+
+ protected String asin(String s) throws LogoError
+ {
+ if (lowPrecision)
+ {
+ return teste_fin_double(Math.toDegrees(Math.asin(numberDouble(s))));
+ }
+ else
+ {
+ BigDecimal bd = numberDecimal(s);
+ return toDegree(asinBD(bd)).toPlainString();
+ }
+ }
+
+ /**
+ * This method returns the exp of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal expBD(BigDecimal bd)
+ {
+ if (!initLogTable)
+ {
+ initLogTable();
+ }
+ int signum = bd.signum();
+ if (signum == -1)
+ {
+ BigDecimal exp = expCordic(bd.negate(mc));
+ exp = BigDecimal.ONE.divide(exp, mc);
+ return exp;
+ }
+ else if (signum == 0)
+ return BigDecimal.ONE;
+ else
+ {
+ return expCordic(bd);
+ }
+ }
+
+ private BigDecimal expCordic(BigDecimal bd)
+ {
+ int i = 0;
+ BigDecimal y = BigDecimal.ONE;
+ while (i < mc.getPrecision())
+ {
+ while (logTable[i].subtract(bd).signum() == -1)
+ {
+ bd = bd.subtract(logTable[i], mc);
+ y = y.add(y.multiply(tenth.pow(i, mc), mc), mc);
+ }
+ i++;
+ }
+ y = y.multiply(bd.add(BigDecimal.ONE, mc), mc);
+ return y;
+
+ }
+
+ /**
+ * This method returns the log of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal logBD(BigDecimal bd)
+ {
+ if (!initLogTable)
+ {
+ initLogTable();
+ }
+ // If bd > 1
+ int signum = bd.subtract(BigDecimal.ONE, mc).signum();
+ if (signum == 1)
+ {
+ bd = bd.subtract(BigDecimal.ONE, mc);
+ return logCordic(bd);
+ }
+ else if (signum == 0)
+ return BigDecimal.ZERO;
+ else
+ {
+ bd = BigDecimal.ONE.divide(bd, mc).subtract(BigDecimal.ONE, mc);
+ return logCordic(bd).negate(mc);
+ }
+ }
+
+ private BigDecimal logCordic(BigDecimal bd)
+ {
+ int i = 0;
+ BigDecimal y = BigDecimal.ZERO;
+ while (i < mc.getPrecision())
+ {
+ BigDecimal tenthi = tenth.pow(i, mc);
+ while (bd.subtract(tenthi, mc).signum() > 0)
+ {
+ bd = bd.subtract(tenthi, mc).divide(BigDecimal.ONE.add(tenthi, mc), mc);
+ y = y.add(logTable[i], mc);
+ }
+ i++;
+ }
+ y = y.add(bd, mc).subtract(bd.pow(2, mc).multiply(new BigDecimal(0.5), mc), mc);
+ return y;
+
+ }
+
+ /**
+ * This method returns the sqrt of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal sqrtBD(BigDecimal bd)
+ {
+ if (bd.signum() == 0)
+ return BigDecimal.ZERO;
+ BigDecimal three = new BigDecimal(3);
+ BigDecimal half = new BigDecimal(0.5);
+ BigDecimal x = BigDecimal.ZERO;
+ BigDecimal y = BigDecimal.ONE.min(BigDecimal.ONE.divide(bd, mc));
+ while (x.compareTo(y) == -1)
+ {
+ x = y;
+ // y=(3x-bd*x^3)/2
+ y = x.multiply(three, mc).subtract(bd.multiply(x.pow(3, mc), mc), mc).multiply(half, mc);
+ }
+ return BigDecimal.ONE.divide(y, mc);
+ }
+
+ /**
+ * This method returns the cos of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal cosBD(BigDecimal bd)
+ {
+ // bd is in degree
+ BigDecimal period = new BigDecimal(360);
+ BigDecimal a90 = new BigDecimal(90);
+ BigDecimal a135 = new BigDecimal(135);
+ BigDecimal a180 = new BigDecimal(180);
+ BigDecimal a225 = new BigDecimal(225);
+ BigDecimal a270 = new BigDecimal(270);
+ BigDecimal a315 = new BigDecimal(315);
+
+ bd = bd.remainder(period, mc);
+ if (bd.signum() == -1)
+ bd = bd.add(period, mc);
+ BigDecimal quarterpi = new BigDecimal(45);
+ // Now bd between 0 and 360.
+ if (bd.compareTo(quarterpi) == -1)
+ {
+ // Between 0 and 45
+ return cosCordic(toRadian(bd));
+ }
+ else if (bd.compareTo(a90) == -1)
+ {
+ // Between 45 and 90
+ return sinCordic(toRadian(a90.subtract(bd, mc)));
+ }
+ else if (bd.compareTo(a135) == -1)
+ {
+ // Between 90 and 135
+ return sinCordic(toRadian(bd.subtract(a90, mc))).negate(mc);
+ }
+ else if (bd.compareTo(a180) == -1)
+ {
+ // Between 135 and 180
+ return cosCordic(toRadian(a180.subtract(bd, mc))).negate(mc);
+ }
+ else if (bd.compareTo(a225) == -1)
+ {
+ // Between 180 and 225
+ return cosCordic(toRadian(bd.subtract(a180, mc))).negate(mc);
+ }
+ else if (bd.compareTo(a270) == -1)
+ {
+ // Between 225 and 270
+ return sinCordic(toRadian(a270.subtract(bd, mc))).negate(mc);
+ }
+ else if (bd.compareTo(a315) == -1)
+ {
+ // Between 270 and 315
+ return sinCordic(toRadian(bd.subtract(a270, mc)));
+ }
+ else
+ {
+ return cosCordic(toRadian(new BigDecimal(360).subtract(bd, mc)));
+ }
+ }
+
+ /**
+ * This method returns the cos of bd with 0<bd<pi/4
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal cosCordic(BigDecimal bd)
+ {
+ return BigDecimal.ONE.divide(sqrtBD(tanCordic(bd).pow(2, mc).add(BigDecimal.ONE, mc)), mc);
+ }
+
+ /**
+ * This method returns the cos of bd
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal sinBD(BigDecimal bd)
+ {
+ BigDecimal a90 = new BigDecimal(90);
+ return cosBD(a90.subtract(bd, mc));
+
+ }
+
+ /**
+ * This method returns the sin of bd with 0<bd<pi/4
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal sinCordic(BigDecimal bd)
+ {
+ BigDecimal tan = tanCordic(bd);
+ return tan.divide(sqrtBD(tan.pow(2, mc).add(BigDecimal.ONE, mc)), mc);
+ }
+
+ /**
+ * This method returns the tan of bd (in degree)
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal tanBD(BigDecimal bd)
+ {
+ // bd is in degree
+ BigDecimal pi = new BigDecimal(180);
+ BigDecimal halfpi = new BigDecimal(90);
+ BigDecimal quarterpi = new BigDecimal(45);
+ bd = bd.remainder(pi, mc);
+ if (bd.compareTo(halfpi.negate(mc)) == -1)
+ bd = bd.add(pi, mc);
+ if (bd.compareTo(halfpi) == 1)
+ bd = bd.subtract(pi, mc);
+ // Now bd is in -90;+90 degrees
+
+ if (bd.compareTo(quarterpi) == 1)
+ {
+ BigDecimal x = toRadian(new BigDecimal(0.5).multiply(bd, mc));
+ return new BigDecimal(2).multiply(tanCordic(x), mc).divide(
+ BigDecimal.ONE.subtract(tanCordic(x).pow(2, mc), mc), mc);
+ }
+ else if (bd.signum() == 1)
+ {
+ return tanCordic(toRadian(bd));
+ }
+ else if (bd.compareTo(quarterpi.negate(mc)) == 1)
+ {
+ return tanCordic(toRadian(bd.negate(mc))).negate(mc);
+ }
+ else
+ {
+ BigDecimal x = toRadian(new BigDecimal(0.5).multiply(bd, mc)).negate(mc);
+ return new BigDecimal(2).multiply(tanCordic(x), mc)
+ .divide(BigDecimal.ONE.subtract(tanCordic(x).pow(2, mc), mc), mc).negate(mc);
+ }
+ }
+
+ /**
+ * This method returns the tan of bd with 0<bd<pi/4
+ * based on the Cordic algorithm
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private BigDecimal tanCordic(BigDecimal bd)
+ {
+ if (!initCosTable)
+ initCosTable();
+ BigDecimal three = new BigDecimal(3);
+ int k = 1;
+ BigDecimal x = BigDecimal.ONE;
+ BigDecimal y = BigDecimal.ZERO;
+ BigDecimal tenthk = tenth;
+ while (k < mc.getPrecision())
+ {
+ while (cosTable[k].compareTo(bd) == -1)
+ {
+ bd = bd.subtract(cosTable[k], mc);
+ BigDecimal tmp = x;
+ x = x.subtract(tenthk.multiply(y, mc), mc);
+ y = y.add(tenthk.multiply(tmp, mc), mc);
+ }
+ tenthk = tenthk.multiply(tenth, mc);
+ k++;
+ }
+ BigDecimal tmp = bd.pow(3, mc).add(three.multiply(bd, mc), mc);
+ // return (3*y+(3t+t^3)*x)/(3x-(3t+t^3)*y
+ return three.multiply(y, mc).add(x.multiply(tmp, mc), mc)
+ .divide(three.multiply(x, mc).subtract(y.multiply(tmp, mc), mc), mc);
+ }
+
+ private BigDecimal piBD()
+ {
+ if (!initCosTable)
+ {
+ initCosTable();
+ }
+ return cosTable[0].multiply(new BigDecimal(4), mc);
+ }
+
+ /**
+ * This method creates the log Table using
+ * log h=(h-1)-(h-1)^2/2+(h-1)^3/3-.....
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private void initLogTable()
+ {
+ initLogTable = true;
+ // calculate ln 2
+ // Using ln 2=2*(x+x^3/3+x^5/5+....) with x=1/3
+
+ BigDecimal sum = BigDecimal.ZERO;
+ BigDecimal previous = BigDecimal.ONE;
+ BigDecimal i = BigDecimal.ONE;
+ BigDecimal nine = new BigDecimal(9);
+ BigDecimal two = new BigDecimal(2);
+ BigDecimal power = new BigDecimal(3);
+ while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0)
+ {
+ previous = sum;
+ sum = sum.add(BigDecimal.ONE.divide(i.multiply(power, mc), mc), mc);
+ i = i.add(two, mc);
+ power = power.multiply(nine, mc);
+ }
+ logTable[0] = sum.multiply(two, mc);
+
+ // Calculate ln (1+10^-j) j in 1 ... digits-1
+
+ for (int j = 1; j < mc.getPrecision(); j++)
+ {
+ // count=0;
+ sum = BigDecimal.ZERO;
+ previous = BigDecimal.ONE;
+ i = BigDecimal.ONE;
+ // 10^(-j)
+ BigDecimal bd = tenth.pow(j, mc);
+ power = bd;
+ while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0)
+ {
+ previous = sum;
+ sum = sum.add(power.divide(i, mc), mc);
+ if (i.signum() == 1)
+ i = i.add(BigDecimal.ONE, mc).negate(mc);
+ else
+ i = i.subtract(BigDecimal.ONE, mc).negate(mc);
+ power = power.multiply(bd, mc);
+ // count++;
+ }
+ logTable[j] = sum;
+ }
+ }
+
+ /**
+ * This method creates the cos Table using
+ * arctan h=x-x^3/3+x^5/5-x^7/7...
+ *
+ * @param bd
+ * The first BigDecimal
+ * @return The result
+ */
+ private void initCosTable()
+ {
+ initCosTable = true;
+ // calculate pi/4
+
+ cosTable[0] = calcPI().multiply(new BigDecimal(0.25), mc);
+
+ // Calculate arctan (10^-j) j in 1 ... digits-1
+
+ for (int j = 1; j < mc.getPrecision(); j++)
+ {
+ // 10^(-j)
+ BigDecimal bd = tenth.pow(j, mc);
+ cosTable[j] = arctanSE(bd);
+ // System.out.println(cosTable[j].toPlainString());
+ }
+ }
+
+ // Using PI = 16arctg(1/5) - 4arctg(1/239)
+ private BigDecimal calcPI()
+ {
+ return new BigDecimal(16).multiply(arctanSE(new BigDecimal("0.2")), mc).subtract(
+ new BigDecimal(4).multiply(arctanSE(BigDecimal.ONE.divide(new BigDecimal(239), mc)), mc), mc);
+ }
+
+ private BigDecimal arctanSE2(BigDecimal bd)
+ {
+ BigDecimal i = BigDecimal.ONE;
+ // BigDecimal j=new BigDecimal(3);
+ BigDecimal two = new BigDecimal(2);
+ BigDecimal square = bd.multiply(bd, mc);
+ BigDecimal power = bd.divide(square.add(BigDecimal.ONE, mc), mc);
+ BigDecimal cst = new BigDecimal(4).multiply(square, mc).divide(square.add(BigDecimal.ONE, mc), mc);
+ BigDecimal previous = BigDecimal.ZERO;
+ BigDecimal sum = power;
+ int count = 0;
+ while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0)
+ {
+ previous = sum;
+ power = power.multiply(cst, mc);
+ power = power.multiply(i.pow(2, mc), mc);
+ BigDecimal doublei = two.multiply(i, mc);
+ doublei = doublei.multiply(doublei.add(BigDecimal.ONE, mc), mc);
+ power = power.divide(doublei, mc);
+ sum = sum.add(power, mc);
+ i = i.add(BigDecimal.ONE);
+ count++;
+ }
+ System.out.println("Itérations " + count);
+ return sum;
+
+ }
+
+ private BigDecimal atanBD(BigDecimal bd)
+ {
+ if (bd.signum() == -1)
+ return atanBD(bd.negate(mc)).negate(mc);
+ if (bd.compareTo(BigDecimal.ONE) == 1)
+ // pi/2 -arctan (1/x)
+ return piBD().multiply(new BigDecimal(0.5), mc).subtract(arctanSE(BigDecimal.ONE.divide(bd, mc)), mc);
+ else if (bd.compareTo(BigDecimal.ONE) == 0)
+ return piBD().multiply(new BigDecimal("0.25"), mc);
+ else
+ return arctanSE(bd);
+ }
+
+ private BigDecimal acosBD(BigDecimal bd)
+ {
+ if (bd.compareTo(new BigDecimal("-1")) == 0)
+ {
+ return piBD();
+ }
+ // acos x= 2 atan (sqrt(1-x^2)/1+x
+ else
+ {
+ return new BigDecimal("2").multiply(
+ atanBD(sqrtBD(BigDecimal.ONE.subtract(bd.pow(2, mc), mc)).divide(BigDecimal.ONE.add(bd, mc), mc)),
+ mc);
+
+ }
+ }
+
+ private BigDecimal asinBD(BigDecimal bd)
+ {
+ // acos x= 2 atan (x/(1+sqrt(1-x^2))
+ return new BigDecimal("2").multiply(
+ atanBD(bd.divide(BigDecimal.ONE.add(sqrtBD(BigDecimal.ONE.subtract(bd.pow(2, mc), mc)), mc), mc)), mc);
+ }
+
+ // arctan h=x-x^3/3+x^5/5-x^7/7...
+ private BigDecimal arctanSE(BigDecimal bd)
+ {
+ BigDecimal i = BigDecimal.ONE;
+ BigDecimal two = new BigDecimal(2, mc);
+ BigDecimal square = bd.multiply(bd, mc);
+ BigDecimal sum = BigDecimal.ZERO;
+ BigDecimal previous = BigDecimal.ONE;
+ while (sum.subtract(previous, mc).abs(mc).compareTo(BigDecimal.ZERO) != 0)
+ {
+ previous = sum;
+ sum = sum.add(bd.divide(i, mc), mc);
+ if (i.signum() == 1)
+ i = i.add(two, mc).negate(mc);
+ else
+ i = i.subtract(two, mc).negate(mc);
+ bd = bd.multiply(square, mc);
+ }
+ return sum;
+ }
+
+ private BigDecimal toRadian(BigDecimal n)
+ {
+ return n.multiply(piBD(), mc).divide(new BigDecimal(180), mc);
+ }
+
+ private BigDecimal toDegree(BigDecimal n)
+ {
+ return n.multiply(new BigDecimal(180), mc).divide(piBD(), mc);
+ }
+
+ /**
+ * This method converts st to double
+ *
+ * @param st
+ * The String
+ * @return The double corresponding to st
+ * @throws LogoError
+ * If st can't be convert
+ */
+
+ protected double numberDouble(String st) throws LogoError
+ { // Si un nombre est
+ // un double
+ try
+ {
+ return (Double.parseDouble(st));
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(st + " " + Logo.messages.getString("pas_nombre"));
+ }
+ }
+
+ /**
+ * If a double ends with the suffix ".0", remove it
+ *
+ * @param d
+ * @return
+ */
+
+ static protected String teste_fin_double(double d)
+ {
+ String st = String.valueOf(d);
+ if (st.endsWith(".0"))
+ st = st.substring(0, st.length() - 2);
+ return st;
+ }
+
+ /**
+ * Converts st to BigDecimal number
+ *
+ * @param st
+ * The String to convert
+ * @return The BigDecimal Number
+ * @throws LogoError
+ * if st isn't a number
+ */
+
+ protected BigDecimal numberDecimal(String st) throws LogoError
+ {
+ /*
+ * if (null==mc){
+ * try {
+ * BigDecimal bd = new BigDecimal(st).setScale(16,
+ * BigDecimal.ROUND_HALF_EVEN);
+ * return (new BigDecimal(eraseZero(bd)));
+ * } catch (NumberFormatException e) {
+ * throw new myException( st + " "
+ * + Logo.messages.getString("pas_nombre"));
+ * }
+ * }
+ * else {
+ */
+ try
+ {
+ return new BigDecimal(st, mc);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(st + " " + Logo.messages.getString("pas_nombre"));
+ }
+ // }
+
+ }
+
+ /**
+ * Erase unused Zeros in decimal Format
+ *
+ * @param bd
+ * The decimal number
+ * @return The formatted number
+ */
+ static protected String eraseZero(BigDecimal bd)
+ {
+ DecimalFormatSymbols dfs = new DecimalFormatSymbols();
+ dfs.setDecimalSeparator('.');
+ DecimalFormat df = new DecimalFormat("#####.################", dfs);
+ String st = df.format(bd);
+ return st;
+
+ }
+
+ /**
+ * Test if the number contained in st is an integer
+ *
+ * @param st
+ * The Object to convert
+ * @return The integer corresponding to st
+ * @throws LogoError
+ * If it isn't an integer
+ */
+
+ protected int getInteger(String st) throws LogoError
+ { // Si c'est un
+ // entier
+ try
+ {
+ return Integer.parseInt(st);
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(st + " " + Logo.messages.getString("pas_entier"));
+ }
+ }
+
+ /**
+ * Test if the number contained in st is an integer
+ *
+ * @param st
+ * The Object to convert
+ * @return The integer corresponding to st
+ * @throws LogoError
+ * If it isn't an integer
+ */
+
+ protected BigDecimal getBigInteger(String st) throws LogoError
+ { // Si c'est un
+ // entier
+ try
+ {
+ return new BigDecimal(new BigInteger(st));
+ }
+ catch (NumberFormatException e)
+ {
+ throw new LogoError(st + " " + Logo.messages.getString("pas_entier"));
+ }
+ }
+
+ protected int getDigits()
+ {
+ if (digits < 0)
+ return -1;
+ else
+ return mc.getPrecision();
+ }
+
+ public static String getOutputNumber(String s)
+ {
+ try
+ {
+ if (digits >= 0 && digits < 16)
+ {
+ BigDecimal bd = new BigDecimal(s);
+ s = bd.toPlainString();
+ // is it a decimal number?
+ int index = s.indexOf(".");
+ if (index != -1)
+ {
+ if (digits == 0)
+ return s.substring(0, index);
+ else if (s.length() > index + digits)
+ {
+ s = s.substring(0, index + digits + 1);
+ int a = Integer.parseInt(String.valueOf(s.charAt(s.length() - 1)));
+ if (a > 4)
+ {
+ a++;
+ s = s.substring(0, s.length() - 1) + a;
+ }
+ return s;
+ }
+ }
+ else
+ return s;
+ }
+ }
+ catch (NumberFormatException e)
+ {}
+ return s;
+ }
+}