Package abstrasy.libraries.math

Source Code of abstrasy.libraries.math.External_Decimal

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);
    }
}
TOP

Related Classes of abstrasy.libraries.math.External_Decimal

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.