//
//  File:   hzNumexp.h
//
//  Legal Notice: This file is part of the HadronZoo C++ Class Library.
//
//  Copyright 2025 HadronZoo Project (http://www.hadronzoo.com)
//
//  The HadronZoo C++ Class Library is free software: You can redistribute it, and/or modify it under the terms of the GNU Lesser General Public License, as published by the Free
//  Software Foundation, either version 3 of the License, or any later version.
//
//  The HadronZoo C++ Class Library 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 Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public License along with the HadronZoo C++ Class Library. If not, see http://www.gnu.org/licenses.
//
//
//  This package provides a means of evaluating arithmetic (numerical) expressions. An expression as a minimum, has a single term but otherwise
//  has the general form of 'term operator remainder-of-expression' (effectively term-operator-term). In the single term case, evaluation of an
//  expression is done by evaluating the term and returning the result. In all other cases evaluation of an expression is done by evaluating the
//  first term, then recursing to evaluate the remainder-of-expression as though it were just a second term, and then applying the operator to
//  the to the two evaluations and returning the result.
//
//  Note that only binary operators can be applied to two terms. Unary operators are applied directly to the terms as part of term evaluation.
//
//  Five classes are used as follows:-
//
//  1)  hzNumexpTerm    This is the 'term' and is the base class for two types of term as follows:-
//
//  a)  hzNumexpValue   This is fixed value of any legal data type.
//
//  b)  hzNumexpForm    By having this as a derivative of hzNumexpTerm is what enables expressions to be built in tree form. It is comprised
//                      of a single binary operator and a pair of operands which can be themselves hzNumexpForm instances.
//
//  2)  hzNumexp        This is the class that parses the expression.
//
#ifndef hzNumexp_h
#define hzNumexp_h
/*
**  Other includes and re-emptive class defs
*/
#include "hzTokens.h"
/*
**  Enumerated operators
*/
enum    hzUnary
{
    //  Category:   Expression
    //
    //  Unary operators
    UOP_NULL,       //  No operator
    UOP_NOT,        //  !   reverse bool
    UOP_COMP        //  ~   apply one's compliment
} ;
enum    hzOperator
{
    //  Category:   Expression
    //
    //  Binary operators
    OP_NULL,        //  No operator
    OP_ASSIGN,      //  =
    OP_EQUAL,       //  ==
    OP_GT,          //  >
    OP_LT,          //  <
    OP_GTEQ,        //  >=
    OP_LTEQ,        //  <=
    OP_PLUS,        //  +
    OP_MINUS,       //  -
    OP_MULT,        //  *
    OP_DIVIDE,      //  /
    OP_POWER,       //  ^
    OP_AND,         //  &&
    OP_OR           //  ||
} ;
class   hzNumexpTerm
{
    //  Category:   Expression
    //
    //  Base class unifying the three primary entities of constants, references and formulae
public:
    virtual void    SetUnaryOp  (hzUnary uop) = 0 ;
    virtual double  Evaluate    (void) = 0 ;
} ;
class   hzNumexpValue   : public hzNumexpTerm
{
    //  Category:   Expression
    //
    //  The hzNumexpValue class is a value of any legal HadronZoo type
protected:
    double  m_Atom ;        //  Result of term eveluation
    hzUnary m_eUnary ;      //  Unary operator to apply to the result (if applicable)
public:
    hzNumexpValue   (void)  {}
    ~hzNumexpValue  (void)  {}
    void    operator=   (double v)      { m_Atom = v ; }
    void    SetUnaryOp  (hzUnary eUOP)  { m_eUnary = eUOP ; }
    double  Evaluate    (void)          { return m_Atom ; }
} ;
/*
**  The hzNumexpForm class provides the method of parenthesis
*/
class   hzNumexpForm    : public hzNumexpTerm
{
    //  Category:   Expression
    //
    //  The hzNumexpForm class provides the method of parenthesis by allowing two operands to be tied to a binary operator. The operands
    //  can themselves be formulae and so any expression can be represented as a series of hzNumexpForm instance. Any unary operations
    //  are to be applied to the formula as a whole and not to the individual operands.
protected:
    double      m_Result ;          //  Result of expression evaluation
    hzNumexpTerm*   m_pA ;          //  First term in numeric expression
    hzNumexpTerm*   m_pB ;          //  Second term in numeric expression
    hzUnary     m_eUnary ;          //  Unary operator to apply to the result (if applicable)
    hzOperator  m_eBinary ;         //  Operator to apply to the two terms
public:
    hzNumexpForm    (void)  {}
    ~hzNumexpForm   (void)  {}
    //  These called by hzNumexp::Parse()
    void    SetUnaryOp  (hzUnary uop)       { m_eUnary = uop ; }
    void    SetOperator (hzOperator bOp)    { m_eBinary = bOp ; }
    bool    AddOperand  (hzNumexpTerm* pOperand)
    {
        if (!m_pA)
            { m_pA = pOperand ; return true ; }
        if (!m_pB)
            { m_pB = pOperand ; return true ; }
        return false ;
    }
    double  Evaluate    (void) ;
} ;
class   hzNumexp
{
    //  Category:   Expression
    //
    //  The hzNumexp class can now evaluate an unlimited tree of constants, variables and expressions in parenthesis.
protected:
    hzVect<hzToken> m_Tokens ;          //  Tokens of numeric expression
    hzNumexpTerm*   m_pRoot ;           //  Expression root
    hzChain         m_Error ;           //  Error reporting
    double          m_Default ;         //  Default value
    uint32_t        m_nParLevel ;       //  Current level in expression tree
    uint32_t        m_tokIter ;         //  Token iterator
    //uint32_t  _ProcessReference   (void) ;
public:
    hzNumexp    (void)
    {
        m_pRoot = 0 ;
        m_nParLevel = 0 ;
    }
    ~hzNumexp   (void)  {}
    hzChain&    Show    (void)  { return m_Error ; }
    double  Evaluate    (void) ;
    hzEcode Parse       (const char* pExpression) ;
    hzNumexpTerm*   Parse   (void) ;
} ;
#endif  //  hzNumexp_h