package abstrasy.libraries.math;
import abstrasy.Bivalence;
import abstrasy.Node;
import abstrasy.PCoder;
import abstrasy.SELF;
import abstrasy.externals.AExtCachable;
import abstrasy.externals.AExtClonable;
import abstrasy.externals.AExtTools;
import abstrasy.externals.AExtVObject;
import abstrasy.interpreter.InterpreterException;
import abstrasy.interpreter.ORef;
import abstrasy.interpreter.StdErrors;
import abstrasy.libraries.math.rjm.BigDecimalMath;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.math.MathContext;
import java.math.RoundingMode;
import java.util.concurrent.atomic.AtomicReference;
/**
* Abstrasy Interpreter
*
* Copyright : Copyright (c) 2006-2012, Luc Bruninx.
*
* Concédée sous licence EUPL, version 1.1 uniquement (la «Licence»).
*
* Vous ne pouvez utiliser la présente oeuvre que conformément à la Licence.
* Vous pouvez obtenir une copie de la Licence à l’adresse suivante:
*
* http://www.osor.eu/eupl
*
* Sauf obligation légale ou contractuelle écrite, le logiciel distribué sous
* la Licence est distribué "en l’état", SANS GARANTIES OU CONDITIONS QUELLES
* QU’ELLES SOIENT, expresses ou implicites.
*
* Consultez la Licence pour les autorisations et les restrictions
* linguistiques spécifiques relevant de la Licence.
*
*
* @author Luc Bruninx
* @version 1.0
*/
public class External_Decimal extends AExtTools implements AExtClonable,AExtCachable,AExtVObject {
/* contexte */
private AtomicReference<MathContext> context = new AtomicReference<MathContext>(External_Context.context);
/* Nombre */
private AtomicReference<BigDecimal> number = new AtomicReference<BigDecimal>(BigDecimal.ZERO);
/**
* Constructeur par défaut
*/
public External_Decimal() {
}
/**
* Implémentation de la méthode init!:
* - ((new Math:Integer) 'init! x) où (:init! x) est équivalent à (:parse! x)
*
* @param startAt
* @return
* @throws Exception
*/
public Node external_mutator_init(Node startAt) throws Exception{
external_mutator_parse(startAt);
return SELF.get(); // retourner une référence de l'objet courrant...
}
/**
* Constructeur pour le clonage rapide
* @param number
*/
public External_Decimal(BigDecimal number, MathContext context) {
this.number.getAndSet(number);
this.context.getAndSet(context);
}
/**
* Méthode implémentation du clonage
* @param bival
* @return
*/
@Override
public Object clone_my_self(Bivalence bival) {
External_Decimal t = new External_Decimal();
BigDecimal n = new BigDecimal(0, context.get());
t.number.getAndSet(n.add(number.get()));
return t;
}
/**
* Sérialisation du Decimal...
* @param startAt
* @return
* @throws Exception
*/
public Node external_quote(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1,2);
Node res = null;
MathContext mc=context.get();
res = AExtTools.createNewExpr(Node.createPattern(External_Decimal.class.getName()), Node.createLazy()
.append(AExtTools.createSExpression(":set-precision!", new Node[] { new Node(mc.getPrecision()) }))
.append(AExtTools.createSExpression(":set-RMODE!", new Node[] { new Node(External_Context.RMODEof(mc.getRoundingMode())) }))
.append(AExtTools.createSExpression(":parse!", new Node[] { new Node(number.get().toPlainString()) }))
);
return res;
}
/**
* Etablir le mode d'arrondi pour ce nombre (>=1).
* @param startAt
* @return
* @throws Exception
*/
public Node external_mutator_set_RMODE(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
SELF.require_SELF_mutable();
int arg0 = (int)startAt.getSubNode(1, Node.TYPE_NUMBER).getNumber();
/*
* la précision doit être >= 1...
*/
context.getAndSet( new MathContext(context.get().getPrecision(),RoundingMode.valueOf(arg0)) );
return null;
}
/**
* Obtenir le mode d'arrondi du nombre.
* @param startAt
* @return
* @throws Exception
*/
public Node external_get_RMODE(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return new Node(External_Context.RMODEof(context.get().getRoundingMode()));
}
/**
* Etablir la précision pour ce nombre (>=1).
* @param startAt
* @return
* @throws Exception
*/
public Node external_mutator_set_precision(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
SELF.require_SELF_mutable();
int p = (int)startAt.getSubNode(1, Node.TYPE_NUMBER).getNumber();
/*
* la précision doit être >= 0...
*/
if(p<0) throw new InterpreterException(StdErrors.extend(StdErrors.Out_of_range, "precision "+p+" must be >= 0" ));
BigDecimal r=number.get();
int digits=r.precision()-r.scale();
MathContext mc=context.get();
MathContext nmc = new MathContext(digits<0 ? p+1 : p + digits, mc.getRoundingMode()); // more precision...
context.getAndSet( nmc );
return null;
}
/**
* Obtenir le degré de précision (nombre de décimales significatives)
* @param startAt
* @return
* @throws Exception
*/
public Node external_get_precision(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
BigDecimal r=number.get();
int digits=r.precision()-r.scale();
MathContext mc=context.get();
return new Node(digits<0 ? Math.abs(digits)-1 : mc.getPrecision() - digits);
}
/**
* Retourne un nouveau Decimal avec la précision indiquée.
* Peut servir aussi pour obtenir l'arrondi d'une valeur (en tenant compte du mode d'arrondi par défaut du contexte courant).
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_precision(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
int c=(int)startAt.getSubNode(1, Node.TYPE_NUMBER).getNumber();
if(c<0) throw new InterpreterException(StdErrors.extend(StdErrors.Out_of_range, "precision "+c+" must be >= 0" ));
BigDecimal r=number.get();
int digits=r.precision()-r.scale();
MathContext mc=context.get();
MathContext nmc = new MathContext(digits<0 ? c+1 : c + digits, mc.getRoundingMode()); // more precision...
return Node.createExternal(new External_Decimal(number.get().round(nmc),nmc));
}
/**
* Applique la précision indiquée au Decimal (de manière statique).
* Peut servir aussi pour obtenir l'arrondi d'une valeur (en tenant compte du mode d'arrondi par défaut du contexte courant).
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_mutator_precision(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
int c=(int)startAt.getSubNode(1, Node.TYPE_NUMBER).getNumber();
if(c<0) throw new InterpreterException(StdErrors.extend(StdErrors.Out_of_range, "precision "+c+" must be >= 0" ));
BigDecimal r=number.get();
int digits=r.precision()-r.scale();
MathContext mc=context.get();
context.getAndSet( new MathContext(digits<0 ? c+1 : c + digits, mc.getRoundingMode()) );
number.getAndSet(number.get().round(context.get()));
return null;
}
/**
* Analyseur / convertiseur vers le type Decimal.
*
* Remarque: le contexte est mis à jour si la précision est insuffisante.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_mutator_parse(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
SELF.require_SELF_mutable();
Node arg0 = startAt.getSubNode(1, Node.TYPE_NUMBER | Node.TYPE_STRING | Node.TYPE_NAMESPACE);
if (arg0.isNumber()) {
//number
number.getAndSet( new BigDecimal(arg0.getNumber(),context.get()) );
}
else{
//string et objet...
String sn=Node.node2VString(arg0).getString();
MathContext mc=context.get();
BigDecimal v=new BigDecimal(sn);
if(v.precision()>mc.getPrecision())
context.getAndSet( new MathContext(v.precision(),mc.getRoundingMode()) );
number.getAndSet( v );
}
return null;
}
/**
* Retourne le Decimal sous la forme d'un number.
*
* @param startAt
* @return Node:number
* @throws Exception
*/
public Node external_number(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return new Node(number.get().doubleValue());
}
/**
* Retourne le Decimal sous la forme d'une chaîne de caractères dans la forme complète.
*
* @param startAt
* @return Node:string
* @throws Exception
*/
public Node external_string(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
BigDecimal n = number.get();
//int digits=Math.abs(n.precision()-n.scale());
//System.out.println("integer part:"+digits+", precision:"+number.precision());
MathContext mc=context.get();
return new Node(n.round(new MathContext(Math.max(n.precision(),mc.getPrecision()),mc.getRoundingMode())).stripTrailingZeros().toPlainString());
}
/**
* Retourne le Decimal sous la forme d'une chaîne de caractères dans la Engineering (avec puissances de 10).
*
* @param startAt
* @return Node:string
* @throws Exception
*/
public Node external_string_E(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
BigDecimal n = number.get();
int digits=BigDecimalMath.getIntDigitsCnt(n);
MathContext mc=context.get();
return new Node(n.round(new MathContext(digits+mc.getPrecision(),mc.getRoundingMode())).stripTrailingZeros().toEngineeringString());
}
/*
*
*/
private final BigDecimal _convertArg_(Node arg) throws InterpreterException, Exception {
if (arg.isNumber()) {
//number
return new BigDecimal(arg.getNumber(), context.get());
}
else if (arg.isString()) {
//string
String sn=arg.getString();
BigDecimal v=new BigDecimal(sn);
MathContext mc=context.get();
if(v.precision()<mc.getPrecision())
return new BigDecimal(sn,mc);
else
return v;
}
else if(arg.isDelegable()){
//rechercher les optimisation pour les types external
if(arg.getQType()==Node.TYPE_EXTERNAL){
Object o = arg.getExternal();
if (o instanceof External_Decimal) {
// optimisation pour Decimal
return ((External_Decimal) o).number.get();
}
else if (o instanceof External_Integer) {
// optimisation pour Integer
return ((External_Integer) o).toDecimal();
}
}
// pas d'optimisation...
// alors résolution générale...
// (:to-decimal)
Node r = Node.VDelegable.evalMethod_or_null(arg, "to-decimal", null);
if (r != null)
return _convertArg_(r);
// (:number)
r = Node.VDelegable.evalMethod_or_null(arg, PCoder.getMethod(PCoder.PC_NUMBER), null);
if (r != null)
return _convertArg_(r);
}
//sinon, et bien...
throw new InterpreterException(StdErrors.extend(StdErrors.Invalid_parameter,
"This object do not implement :to-decimal method (" + arg.toString() +")"));
}
private final BigDecimal _getArg_(Node startAt, int i) throws Exception {
return _convertArg_(startAt.getSubNode(i, Node.TYPE_NUMBER | Node.TYPE_STRING | Node.VTYPE_DELEGABLE));
}
/*
*
*/
private final BigDecimal _getArg_lazy_(Node startAt, int i) throws Exception {
return _convertArg_( startAt.getSubNode_LazyValue(i, Node.TYPE_NUMBER | Node.TYPE_STRING | Node.VTYPE_DELEGABLE) );
}
/*
*
*/
/**
* Retourne la somme du Decimal courant ajouté de la liste de valeurs qui peuvent être des Nodes.
*
* Les types d'entrées supportées sont: number, string et Decimal.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_add(Node startAt) throws Exception {
startAt.isGoodArgsLength(false, 2);
BigDecimal r = number.get();
for (int i = 1; i < startAt.size(); i++) {
r = r.add(_getArg_(startAt, i));
}
return Node.createExternal(new External_Decimal(r, context.get()));
}
/**
* surcharge de l'opérateur +!
*
* Les types d'entrées supportées sont: number, string, Integer et Decimal.
*
* @param startAt
* @return
* @throws Exception
*/
public Node external_mutator_add(Node startAt) throws Exception {
SELF.require_SELF_mutable();
startAt.isGoodArgsLength(false, 2);
BigDecimal[] v = new BigDecimal[startAt.size() - 1];
for (int i = 1; i < startAt.size(); i++) {
v[i - 1] = _getArg_(startAt, i);
//System.out.println(v[i-1].toPlainString());
}
BigDecimal old_r;
BigDecimal r;
do {
old_r = number.get();
r = old_r;
for (int i = 0; i < v.length; i++)
r = r.add(v[i]); // , new MathContext(Math.max(r.precision(),v[i].precision()),context.get().getRoundingMode()) );
//System.out.println(r.toPlainString());
}
while (!number.compareAndSet(old_r, r));
return null;
}
/**
* Retourne le produit du Decimal courant multiplié par la liste des valeurs posées en arguments.
*
* Les types d'entrées supportées sont: number, string et Decimal.
*
* La méthode supporte également les arguments lazy (dont l'évaluation est paresseuse).
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_mul(Node startAt) throws Exception {
startAt.isGoodArgsLength(false, 2);
BigDecimal r = number.get();
if(r.signum()==0){
// cas ou la variable statique est déjà zéro.
return Node.createExternal(new External_Decimal(r,context.get()));
}
int i = 1;
int asize = startAt.size();
while (i < asize && r.signum() != 0)
r = r.multiply(_getArg_lazy_(startAt, i++));
// signum() retourne 0 si r vaut 0. Cette méthode est plus rapide que compareTo()...
return Node.createExternal(new External_Decimal(r,context.get()));
}
/**
* surcharge de l'opérateur *!
*
* Les types d'entrées supportées sont: number, string, Integer et Decimal.
*
* La méthode supporte également les arguments lazy (dont l'évaluation est paresseuse).
*
* @param startAt
* @return
* @throws Exception
*/
public Node external_mutator_mul(Node startAt) throws Exception {
SELF.require_SELF_mutable();
startAt.isGoodArgsLength(false, 2);
if(number.get().signum()==0){
// cas ou la variable statique est déjà zéro.
return null;
}
BigDecimal[] v = new BigDecimal[startAt.size() - 1];
for (int i = 1; i < startAt.size(); i++) {
v[i - 1] = _getArg_lazy_(startAt, i);
if(v[i-1].signum()==0){
// optimisation en cas de multiplication par zéro...
number.getAndSet(BigDecimal.ZERO);
return null;
}
}
BigDecimal old_r;
BigDecimal r;
do {
old_r = number.get();
r = old_r;
int i = 0;
while (i < v.length && r.signum() != 0)
r = r.multiply(v[i++]);
}
while (!number.compareAndSet(old_r, r));
return null;
}
/**
* Retourne le résultat de la soustraction des valeurs d'entrées à partir de la valeur courante.
*
* Note: S'il n'y a pas d'arguments, la méthode retourne -n (n étant la valeur courante).
*
* Les types d'entrées supportées sont: number, string et Decimal.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_sub(Node startAt) throws Exception {
/*
* s'il n'y a pas d'arguments, on considère qu'il s'agit de l'expression (- n)...
*/
int asize = startAt.size();
if (asize == 1) {
return Node.createExternal(new External_Decimal(number.get().negate(), context.get()));
}
/*
* s'il y a au moins un argument, on considère la forme (- n [args...])...
*/
BigDecimal r = number.get();
for (int i = 1; i < asize; )
r = r.subtract(_getArg_(startAt, i++));
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(r.round(mc), mc));
}
/**
* surcharge de l'opérateur -!
*
* Note: S'il n'y a pas d'arguments, la méthode effectue l'opération -n (n étant la valeur courante).
*
* Les types d'entrées supportées sont: number, string, Integer et Decimal.
*
* @param startAt
* @return
* @throws Exception
*/
public Node external_mutator_sub(Node startAt) throws Exception {
/*
* s'il n'y a pas d'arguments, on considère qu'il s'agit de l'expression (- n)...
*/
SELF.require_SELF_mutable();
int asize = startAt.size();
if (asize == 1) {
BigDecimal old_r;
BigDecimal r;
do {
old_r = number.get();
r = old_r.negate();
}
while (!number.compareAndSet(old_r, r));
return null;
}
/*
* s'il y a au moins un argument, on considère la forme (- n [args...])...
*/
BigDecimal[] v = new BigDecimal[startAt.size() - 1];
for (int i = 1; i < startAt.size(); i++) {
v[i - 1] = _getArg_(startAt, i);
}
BigDecimal old_r;
BigDecimal r;
do {
old_r = number.get();
r = old_r;
for (int i = 0; i < v.length; i++)
r = r.subtract(v[i]);
}
while (!number.compareAndSet(old_r, r));
return null;
}
/**
* Renvoie le résultat de la division successive des arguments à partir de la valeur courante.
*
* Note: S'il n'y à pas d'argument, la méthode retourne le résultat de l'opération 1/n (n étant la valeur courante).
*
* Les types d'entrées supportées sont : number, string et Decimal.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_div(Node startAt) throws Exception {
/*
* s'il n'y a pas d'arguments, on considère qu'il s'agit de l'expression (- n)...
*/
int asize = startAt.size();
if (asize == 1) {
MathContext mc=context.get();
BigDecimal d = new BigDecimal(1, mc);
/**
* Attention! La division doit être limitée par me contexte, sinon une division comme 1/3 donne lieu à une execption.
*/
try {
BigDecimal v=number.get();
int digits=Math.abs(v.precision()-v.scale());
MathContext nmc = new MathContext(mc.getPrecision() + digits, mc.getRoundingMode()); // more precision...
return Node.createExternal(new External_Decimal(d.divide(number.get(), nmc),mc));
}
catch (ArithmeticException ex) {
// prévoir la gestion de l'exception division par zéro sur exception seulement.
// c'est plus rapide car on n'a pas besoin de tester la valeur à chaque fois.
if (ex.getMessage().equals("Division by zero")) {
throw new InterpreterException(StdErrors.Division_by_zero);
}
else {
throw new InterpreterException(StdErrors.extend(StdErrors.Arithmetic_error, ex.getMessage()));
}
}
}
/*
* s'il y a au moins un argument, on considère la forme (- n [args...])...
*/
BigDecimal r = number.get();
MathContext mc = context.get();
try {
/**
* Attention! La division doit être limitée par me contexte, sinon une division comme 1/3 donne lieu à une execption.
*/
MathContext nmc = new MathContext(mc.getPrecision() + 2, mc.getRoundingMode()); // more precision...
for (int i = 1; i < asize; ){
BigDecimal v=_getArg_(startAt, i++);
int digits0=Math.abs(r.precision()-r.scale());
int digits1=Math.abs(v.precision()-v.scale());
nmc = new MathContext(nmc.getPrecision() + digits0 + digits1, mc.getRoundingMode()); // more precision...
r = r.divide(v, nmc);
}
}
catch (ArithmeticException ex) {
// prévoir la gestion de l'exception division par zéro sur exception seulement.
// c'est plus rapide car on n'a pas besoin de tester la valeur à chaque fois.
if (ex.getMessage().equals("Division by zero")) {
throw new InterpreterException(StdErrors.Division_by_zero);
}
else {
throw new InterpreterException(StdErrors.extend(StdErrors.Arithmetic_error, ex.getMessage()));
}
}
return Node.createExternal(new External_Decimal(r,mc));
}
/**
* surcharge de l'opérateur /!
*
* Note: S'il n'y à pas d'argument, la méthode effectue de l'opération 1/n (n étant la valeur courante).
*
* Les types d'entrées supportées sont : number, string, Integer et Decimal.
*
* @param startAt
* @return
* @throws Exception
*/
public Node external_mutator_div(Node startAt) throws Exception {
/*
* s'il n'y a pas d'arguments, on considère qu'il s'agit de l'expression (- n)...
*/
SELF.require_SELF_mutable();
int asize = startAt.size();
if (asize == 1) {
BigDecimal d = BigDecimal.ONE;
try {
BigDecimal old_r;
BigDecimal r;
do {
old_r = number.get();
r = d.divide(old_r,context.get());
}
while (!number.compareAndSet(old_r, r));
}
catch (ArithmeticException ex) {
// prévoir la gestion de l'exception division par zéro sur exception seulement.
// c'est plus rapide car on n'a pas besoin de tester la valeur à chaque fois.
if (ex.getMessage().equals("Division by zero")) {
throw new InterpreterException(StdErrors.Division_by_zero);
}
else {
throw new InterpreterException(StdErrors.extend(StdErrors.Arithmetic_error, ex.getMessage()));
}
}
return null;
}
/*
* s'il y a au moins un argument, on considère la forme (- n [args...])...
*/
try {
BigDecimal[] v = new BigDecimal[startAt.size() - 1];
for (int i = 1; i < startAt.size(); i++) {
v[i - 1] = _getArg_(startAt, i);
}
/**
* Attention! La division doit être limitée par me contexte, sinon une division comme 1/3 donne lieu à une execption.
*/
MathContext mc=context.get();
MathContext nmc=mc;
BigDecimal old_r;
BigDecimal r;
do {
old_r = number.get();
r = old_r;
for (int i = 0; i < v.length; i++){
int digits0=Math.abs(r.precision()-r.scale());
int digits1=Math.abs(v[i].precision()-v[i].scale());
nmc = new MathContext(nmc.getPrecision() + digits0 + digits1, mc.getRoundingMode()); // more precision...
r = r.divide(v[i],nmc);
}
}
while (!number.compareAndSet(old_r, r));
}
catch (ArithmeticException ex) {
// prévoir la gestion de l'exception division par zéro sur exception seulement.
// c'est plus rapide car on n'a pas besoin de tester la valeur à chaque fois.
if (ex.getMessage().equals("Division by zero")) {
throw new InterpreterException(StdErrors.Division_by_zero);
}
else {
throw new InterpreterException(StdErrors.extend(StdErrors.Arithmetic_error, ex.getMessage()));
}
}
return null;
}
/**
* Retourne le reste de la division de la valeur courante avec l'argument.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_mod(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
try {
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(number.get().remainder(_getArg_(startAt, 1),mc),mc));
}
catch (ArithmeticException ex) {
// prévoir la gestion de l'exception division par zéro sur exception seulement.
// c'est plus rapide car on n'a pas besoin de tester la valeur à chaque fois.
if (ex.getMessage().equals("Division by zero")) {
throw new InterpreterException(StdErrors.Division_by_zero);
}
else {
throw new InterpreterException(StdErrors.extend(StdErrors.Arithmetic_error, ex.getMessage()));
}
}
}
/**
* retourne un Node:nombre -1, 0 ou 1 selon que la valeur soit négative, nulle ou positive.
*
* @param startAt
* @return Node:number
* @throws Exception
*/
public Node external_sgn(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return new Node(number.get().signum());
}
/**
* retourne un Node:nombre indiquant le nombre de chiffres (puissance de 10 du Decimal).
*
* @param startAt
* @return Node:number
* @throws Exception
*/
public Node external_digits10(Node startAt) throws Exception {
//int digits;
//BigInteger value=number.abs().toBigInteger();
//for (digits = 1; value.compareTo(BigInteger.ONE)>0; digits++) value=value.divide(BigInteger.TEN);
return new Node(BigDecimalMath.getIntDigitsCnt(number.get()));
}
/**
* retourne un Node:Decimal qui correspond à la partie fractionnaire du Decimal courant.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_fractional(Node startAt) throws Exception {
BigDecimal num = number.get();
boolean negatif = num.signum()==-1;
BigDecimal n=num.abs();
BigDecimal n2 = BigDecimalMath.floor(n);
n = n.subtract(n2);
n = negatif ? n.negate(): n;
return Node.createExternal(new External_Decimal(n,context.get()));
}
/**
* retourne un Node:Decimal qui correspond à la partie tronquée (sans arrondi) du Decimal courant.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_truncate(Node startAt) throws Exception {
BigDecimal num = number.get();
boolean negatif = num.signum()==-1;
BigDecimal n=num.abs();
BigDecimal n2 = BigDecimalMath.floor(n);
n = negatif ? n2.negate(): n2;
return Node.createExternal(new External_Decimal(n,context.get()));
}
/**
* retourne la valeur absolue du Decimal.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_abs(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return Node.createExternal(new External_Decimal(number.get().abs(),context.get()));
}
/**
* retourne la valeur Decimal arrondie à l'entier inférieur.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_floor(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return Node.createExternal(new External_Decimal(BigDecimalMath.floor(number.get()),context.get()));
}
/**
* retourne la valeur Decimal arrondie à l'entier spérieur.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_ceil(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return Node.createExternal(new External_Decimal(BigDecimalMath.ceil(number.get()),context.get()));
}
/**
* retourne la valeur Decimal arrondie à l'entier le plus proche.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_round(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return Node.createExternal(new External_Decimal(BigDecimalMath.round(number.get()),context.get()));
}
/**
* méthode réservée pour faciliter la conversion des types...
*
* @return BigInteger
*/
public BigInteger toInteger(){
BigDecimal num = number.get();
boolean negatif = num.signum()==-1;
BigDecimal n=num.abs();
BigInteger n2 = BigDecimalMath.round(n).toBigInteger();
return negatif ? n2.negate(): n2;
}
/**
* retourne un Node:Integer qui correspond à la partie tronquée (avec arrondi "round") du Decimal courant.
*
* @param startAt
* @return Node:Integer
* @throws Exception
*/
public Node external_to_integer(Node startAt) throws Exception {
return Node.createExternal(new External_Integer(toInteger()));
}
/**
* Retourne un Node:number -1, si l'élément comparé est plus petit, 1 si l'élément comparé est plus grand, et 0 s'il s'ont de valeur
* égale.
*
* Est utilisé par =?, >?, <?, <=?, =>? et <>?....
*
* @param startAt
* @return Node:number
* @throws Exception
*/
public Node external_compare(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
return new Node(number.get().compareTo(_getArg_(startAt, 1)));
}
public Node external_is_equ(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
return new Node(number.get().equals(_getArg_(startAt, 1)) ? Node.TRUE:Node.FALSE);
}
/**
* retourne TRUE si la valeur du nombre est zéro.
*
* @param startAt
* @return Node:number
* @throws Exception
*/
public Node external_is_zero(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return new Node(number.get().signum() == 0 ? Node.TRUE: Node.FALSE);
}
/**
* retourne TRUE si la valeur du nombre est négative.
*
* @param startAt
* @return Node:number
* @throws Exception
*/
public Node external_is_negative(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return new Node(number.get().signum() < 0 ? Node.TRUE: Node.FALSE);
}
/**
* retourne TRUE si le nombre est positif.
*
* @param startAt
* @return
* @throws Exception
*/
public Node external_is_positive(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
return new Node(number.get().signum() > 0 ? Node.TRUE: Node.FALSE);
}
/**
* retourne TRUE si la valeur entière du nombre est impaire (non strict).
*
* (c-à-d tous les autres cas où :even? n'est pas vérifiée) !!!
*
* @param startAt
* @return Node:number
* @throws Exception
*/
public Node external_is_odd(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
BigDecimal two=new BigDecimal(2,mc);
BigDecimal a=number.get().divide(two,mc);
BigDecimal b=BigDecimalMath.round(a);
return new Node(a.compareTo(b)!=0 ? Node.TRUE: Node.FALSE);
}
/**
* retourne TRUE si la valeur entière du nombre est paire (strict).
*
* (c-à-d tous les autres cas où :odd? n'est pas vérifiée) !!!
*
* @param startAt
* @return Node:number
* @throws Exception
*/
public Node external_is_even(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
BigDecimal two=new BigDecimal(2,mc);
BigDecimal a=number.get().divide(two,mc);
BigDecimal b=BigDecimalMath.round(a);
return new Node(a.compareTo(b)!=0 ? Node.FALSE: Node.TRUE);
}
/**
* retourne TRUE si le nombre est un entier
*
* @param startAt
* @return Node:number
* @throws Exception
*/
public Node external_is_integer(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
BigDecimal num = number.get();
return new Node(num.compareTo(BigDecimalMath.round(num))==0 ? Node.TRUE: Node.FALSE);
}
/**
* retourne TRUE si le nombre comporte une partie décimale (ce n'est pas un entier).
*
* @param startAt
* @return Node:number
* @throws Exception
*/
public Node external_is_decimal(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
BigDecimal num = number.get();
return new Node(num.compareTo(BigDecimalMath.round(num))!=0 ? Node.TRUE: Node.FALSE);
}
/**
* retourne le cosinus du Decimal courant.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_cos(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.cos(number.get(),mc),mc));
}
/**
* retourne le sinus du Decimal courant.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_sin(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.sin(number.get(),mc),mc));
}
/**
* retourne la tangente du Decimal courant.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_tan(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.tan(number.get(),mc),mc));
}
/**
* retourne la racine carrée du Decimal courant.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_sqrt(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.sqrt(number.get(),mc),mc));
}
/**
* retourne l'exponentielle du Decimal courant soit pow(E,x)...
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_exp(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.exp(number.get(), mc),mc));
}
/**
* retourne le logarithme naturel du Decimal courant.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_log(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.log(number.get(),mc),mc));
}
/**
* retourne le logarithme décimal (base 10) du Decimal courant.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_log10(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.log10(number.get(),mc),mc));
}
/**
* Retourne la valeur du décimal courant élevé à la puissance n.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_pow(Node startAt) throws Exception {
startAt.isGoodArgsCnt(2);
try {
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.pow(number.get(),_getArg_(startAt, 1),mc),mc));
}
catch (ArithmeticException ex) {
// prévoir la gestion de l'exception division par zéro sur exception seulement.
// c'est plus rapide car on n'a pas besoin de tester la valeur à chaque fois.
if (ex.getMessage().equals("Division by zero")) {
throw new InterpreterException(StdErrors.Division_by_zero);
}
else {
throw new InterpreterException(StdErrors.extend(StdErrors.Arithmetic_error, ex.getMessage()));
}
}
}
/**
* retourne la arc-tangente du Decimal courant.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_atan(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.atan(number.get(),mc),mc));
}
/**
* retourne l'arc-cosinus du Decimal courant.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_acos(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.acos(number.get(),mc),mc));
}
/**
* retourne l'arc-sinus du Decimal courant.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_asin(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.asin(number.get(),mc),mc));
}
/**
* retourne la valeur convertie de radians en degrés.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_todegrees(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.toDegrees(number.get(), mc),mc));
}
/**
* retourne la valeur convertie de degrés en radians.
*
* @param startAt
* @return Node:Decimal
* @throws Exception
*/
public Node external_toradians(Node startAt) throws Exception {
startAt.isGoodArgsCnt(1);
MathContext mc=context.get();
return Node.createExternal(new External_Decimal(BigDecimalMath.toRadians(number.get(), mc),mc));
}
/*
* ----------------------------------------------------------------------------
*
* Optimisation par cache d'instanciantion (mars 2012) rev 1.0-6321.0
*
* ----------------------------------------------------------------------------
*/
private static ORef _optim_symbols_cache_ = new ORef();
@Override
public Object getSymbolsCache() {
return _optim_symbols_cache_.getRef();
}
@Override
public void setSymbolsCache(Object cache) {
_optim_symbols_cache_.setRef(cache);
}
}