/*

    KCalc, a scientific calculator for the X window system using the
    Qt widget libraries, available at no cost at http://www.troll.no

    Copyright (C) 1996 Bernd Johannes Wuebben
                       wuebben@math.cornell.edu

    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.

*/

#ifndef _KCALC_CORE_H
#define _KCALC_CORE_H

#include <QStack>
#include "stats.h"
#include "knumber.h"

#define  POS_ZERO  1e-19L  /* What we consider zero is */
#define  NEG_ZERO -1e-19L  /* anything between these two */


typedef KNumber(*Arith)(const KNumber &, const KNumber &);
typedef KNumber(*Prcnt)(const KNumber &, const KNumber &);

#define UNUSED(x) ((void)(x))



struct operator_data {
    int precedence;  // priority of operators in " enum Operation"
    Arith arith_ptr;
    Prcnt prcnt_ptr;
};

class CalcEngine
{
public:
    // operations that can be stored in calculation stack
    enum Operation {
        FUNC_EQUAL,
        FUNC_PERCENT,
        FUNC_BRACKET,
        FUNC_OR,
        FUNC_XOR,
        FUNC_AND,
        FUNC_LSH,
        FUNC_RSH,
        FUNC_ADD,
        FUNC_SUBTRACT,
        FUNC_MULTIPLY,
        FUNC_DIVIDE,
        FUNC_MOD,
        FUNC_INTDIV,
        FUNC_BINOM,
        FUNC_POWER,
        FUNC_PWR_ROOT
    };

    CalcEngine();

    KNumber lastOutput(bool &error) const;

    void enterOperation(KNumber num, Operation func);


    void ArcCosDeg(KNumber input);
    void ArcCosRad(KNumber input);
    void ArcCosGrad(KNumber input);
    void ArcSinDeg(KNumber input);
    void ArcSinRad(KNumber input);
    void ArcSinGrad(KNumber input);
    void ArcTangensDeg(KNumber input);
    void ArcTangensRad(KNumber input);
    void ArcTangensGrad(KNumber input);
    void AreaCosHyp(KNumber input);
    void AreaSinHyp(KNumber input);
    void AreaTangensHyp(KNumber input);
    void Complement(KNumber input);
    void CosDeg(KNumber input);
    void CosRad(KNumber input);
    void CosGrad(KNumber input);
    void CosHyp(KNumber input);
    void Cube(KNumber input);
    void CubeRoot(KNumber input);
    void Exp(KNumber input);
    void Exp10(KNumber input);
    void Factorial(KNumber input);
    void InvertSign(KNumber input);
    void Ln(KNumber input);
    void Log10(KNumber input);
    void ParenClose(KNumber input);
    void ParenOpen(KNumber input);
    void Reciprocal(KNumber input);
    void SinDeg(KNumber input);
    void SinGrad(KNumber input);
    void SinRad(KNumber input);
    void SinHyp(KNumber input);
    void Square(KNumber input);
    void SquareRoot(KNumber input);
    void StatClearAll(KNumber input);
    void StatCount(KNumber input);
    void StatDataNew(KNumber input);
    void StatDataDel(KNumber input);
    void StatMean(KNumber input);
    void StatMedian(KNumber input);
    void StatStdDeviation(KNumber input);
    void StatStdSample(KNumber input);
    void StatSum(KNumber input);
    void StatSumSquares(KNumber input);
    void TangensDeg(KNumber input);
    void TangensRad(KNumber input);
    void TangensGrad(KNumber input);
    void TangensHyp(KNumber input);

    void Reset();
private:
    KStats stats;

    typedef struct {
        KNumber number;
        Operation operation;
    } _node;

    // Stack holds all operations and numbers that have not yet been
    // processed, e.g. user types "2+3*", the calculation can not be
    // executed, because "*" has a higher precedence than "+", so we
    // need to wait for the next number.
    //
    // In the stack this would be stored as ((2,+),(3,*),...)
    //
    // "enterOperation": If the introduced Operation has lower priority
    // than the preceding operations in the stack, then we can start to
    // evaluate the stack (with "evalStack"). Otherwise we append the new
    // Operation and number to the stack.
    //
    // E.g. "2*3+" evaluates to "6+", but "2+3*" can not be evaluated
    // yet.
    //
    // We also take care of brackets, by writing a marker "FUNC_BRACKET"
    // into the stack, each time the user opens one.  When a bracket is
    // closed, everything in the stack is evaluated until the first
    // marker "FUNC_BRACKET" found.
    QStack<_node> _stack;

    KNumber _last_number;

    bool _percent_mode;


    static const struct operator_data Operator[];

    bool evalStack(void);

    KNumber evalOperation(KNumber arg1, Operation operation, KNumber arg2);

    const KNumber Deg2Rad(const KNumber &x) const {
        return KNumber(2) * KNumber::Pi() / KNumber(360) * x;
    }
    const KNumber Gra2Rad(const KNumber &x) const {
        return KNumber(2)*KNumber::Pi() / KNumber(400) * x;
    }
    const KNumber Rad2Deg(const KNumber &x) const {
        return KNumber(360) / (KNumber(2) * KNumber::Pi()) * x;
    }
    const KNumber Rad2Gra(const KNumber &x) const {
        return KNumber(400) / (KNumber(2)*KNumber::Pi()) * x;
    }

};


#endif  //_KCALC_CORE_H
