/*
GeoGebra - Dynamic Mathematics for Everyone
http://www.geogebra.org
This file is part of GeoGebra.
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.
*/
package org.geogebra.common.kernel.arithmetic;
import java.math.BigDecimal;
import org.geogebra.common.kernel.Kernel;
import org.geogebra.common.kernel.StringTemplate;
import org.geogebra.common.kernel.arithmetic.ExpressionNodeConstants.StringType;
import org.geogebra.common.util.StringUtil;
import org.geogebra.common.util.lang.Unicode;
/**
* MyDouble that returns a certain string in toString(). This is used for
* example for the degree sign in geogebra.parser.Parser.jj: new
* MySpecialDouble(kernel, Math.PI / 180.0d, "\u00b0" );
*
* @author Markus Hohenwarter
*/
public class MySpecialDouble extends MyDouble {
private String strToString;
private final String originalString;
private boolean keepOriginalString;
private boolean isLetterConstant; // for Pi, Euler, or Degree constant
private boolean scientificNotation = false;
private boolean setFromOutside;
/**
* @param kernel
* kernel
* @param val
* value
* @param str
* string representation
*/
public MySpecialDouble(Kernel kernel, double val, String str) {
super(kernel, val);
// Reduce can't handle .5*8
originalString = StringUtil.cannonicNumber(str);
strToString = originalString;
// check if this is a letter constant, e.g. Pi or Euler number
char firstChar = strToString.charAt(0);
isLetterConstant = StringUtil.isLetter(firstChar)
|| firstChar == Unicode.DEGREE_CHAR
|| strToString.equals(Unicode.EULER_GAMMA_STRING)
|| "euler_gamma".equals(strToString);
scientificNotation = strToString.indexOf("E") > 0;
keepOriginalString = !isLetterConstant
&& (scientificNotation || Double.isInfinite(val));
if (keepOriginalString) {
BigDecimal bd = new BigDecimal(strToString);
// avoid E notation for small values
double absVal = Math.abs(val);
if (absVal >= 10E-3 && absVal < 10E7) {
// from GeoGebraCAS we get a String using 15 significant figures
// like 3.14160000000000
// let's remove trailing zeros
bd = bd.stripTrailingZeros();
// no E notation
strToString = bd.toPlainString();
} else {
// use E notation if necessary
strToString = MyDouble.toString(bd);
scientificNotation = strToString.indexOf("E") > 0;
}
}
}
/**
* Copy constructor.
*
* @param sd
* special double to copy
*/
public MySpecialDouble(MySpecialDouble sd) {
super(sd);
originalString = sd.originalString;
strToString = sd.strToString;
keepOriginalString = sd.keepOriginalString;
isLetterConstant = sd.isLetterConstant; // for Pi, Euler, or Degree
// constant
scientificNotation = sd.scientificNotation;
setFromOutside = sd.setFromOutside;
}
@Override
public MySpecialDouble deepCopy(Kernel kernel1) {
if (isEulerConstant()) {
return kernel1.getEulerNumber();
}
MySpecialDouble ret = new MySpecialDouble(this);
ret.kernel = kernel1;
return ret;
}
/**
* Force this number to keep original input
*/
public void setKeepOriginalString() {
keepOriginalString = true;
}
/**
* @return true if this equals E (no tolerance)
*/
public boolean isEulerConstant() {
return MyDouble.exactEqual(getDouble(), Math.E);
}
@Override
public String toString(StringTemplate tpl) {
if (setFromOutside) {
return super.toString(tpl);
}
if (!isLetterConstant) {
// serializing to CAS -- simply print input
if (tpl.hasCASType()) {
return tpl.convertScientificNotationGiac(originalString);
}
// if we are printing result of numeric and user didn't force us to
// use significant digits
// print the original string
if (keepOriginalString
|| (!tpl.useScientific(kernel.useSignificantFigures)
&& !strToString.contains("."))
|| tpl.allowMoreDigits()) {
if (scientificNotation) {
// change 5.1E-20 to 5.1*10^(-20) or 5.1 \cdot 10^{-20}
return tpl.convertScientificNotation(strToString);
}
// keep original string
return strToString;
}
// format double value using kernel settings
return super.toString(tpl);
}
// letter constants for pi, e, or degree character
StringType printForm = tpl.getStringType();
char ch;
switch (printForm) {
case GIAC:
ch = strToString.charAt(0);
switch (ch) {
case Unicode.pi:
return "pi";
case Unicode.DEGREE_CHAR:
return "pi/180";
case Unicode.eulerChar:
if (strToString.equals(Unicode.EULER_GAMMA_STRING)) {
return "euler\\_gamma";
}
return "e";
}
break;
case LATEX:
ch = strToString.charAt(0);
switch (ch) {
case Unicode.pi:
return "\\pi ";
case Unicode.DEGREE_CHAR:
return "^{\\circ}";
case Unicode.eulerChar:
if (strToString.equals(Unicode.EULER_GAMMA_STRING)) {
// approx value
return "\\mathit{e_{\\gamma}}";
}
return "\\textit{e}";
// return Unicode.EULER_STRING;
}
break;
default:
break;
}
return strToString;
}
@Override
public void set(double val) {
super.set(val);
setFromOutside = true;
}
@Override
public boolean equals(Object o) {
return super.equals(o);
}
@Override
public int hashCode() {
return super.hashCode();
}
}