
#ifndef __mathuserfunc_h__
#define __mathuserfunc_h__

#include "yacasbase.h"
#include "lispuserfunc.h"
#include "grower.h"

class PatternClass;

/// A mathematical function defined by several rules.
/// This is the basic class which implements functions in Yacas.
/// Evaluation is done by consulting a set of rewriting rules. The
/// body of the first rule that matches, is evaluated and this gives
/// the result of evaluating the function.

class BranchingUserFunction : public LispArityUserFunction
{
public:

    /// Structure containing name of parameter and whether it is put on hold.
    class BranchParameter : public YacasBase
    {
    public:
        BranchParameter(LispStringPtr aParameter,
                        LispInt aHold=LispFalse)
            : iParameter(aParameter), iHold(aHold) {}
        LispStringPtr iParameter;
        LispInt       iHold;
    };

    /// Abstract base class for rules.
    class BranchRuleBase : public YacasBase
    {
    public:
        virtual ~BranchRuleBase();
        virtual LispBoolean Matches(LispEnvironment& aEnvironment, LispPtr* aArguments) = 0;
        virtual LispInt Precedence() const = 0;
        virtual LispPtr& Body() = 0;
    };

    /// A rule with a predicate.
    /// This rule matches if the predicate evaluates to #LispTrue.
    class BranchRule : public BranchRuleBase
    {
    public:
        virtual ~BranchRule();
        BranchRule(LispInt aPrecedence,LispPtr& aPredicate,LispPtr& aBody)
        {
            iPrecedence = aPrecedence;
            iPredicate.Set(aPredicate.Get());
            iBody.Set(aBody.Get());
        }

	/// Return true if the rule matches.
	/// #iPredicate is evaluated in \a Environment. If the result
	/// IsTrue(), this function returns true.
        virtual LispBoolean Matches(LispEnvironment& aEnvironment, LispPtr* aArguments);

	/// Access #iPrecedence.
        virtual LispInt Precedence() const;

	/// Access #iBody.
        virtual LispPtr& Body();
    protected:
        BranchRule() {};
    protected:
        LispInt iPrecedence;
        LispPtr iBody;
        LispPtr iPredicate;
    };

    /// A rule that always matches.
    class BranchRuleTruePredicate : public BranchRule
    {
    public:
        BranchRuleTruePredicate(LispInt aPrecedence,LispPtr& aBody)
        {
            iPrecedence = aPrecedence;
            iBody.Set(aBody.Get());
        }
	/// Return #LispTrue, always.
        virtual LispBoolean Matches(LispEnvironment& aEnvironment, LispPtr* aArguments);
    };

    /// A rule which matches if the corresponding PatternClass matches.
    class BranchPattern : public BranchRuleBase
    {
    public:
        /// Destructor.
        /// This function contains no code.
        virtual ~BranchPattern();

	/// Constructor.
	/// \param aPrecedence precedence of the rule
	/// \param aPredicate generic object of type \c Pattern
	/// \param aBody body of the rule
        BranchPattern(LispInt aPrecedence,LispPtr& aPredicate,LispPtr& aBody)
        {
            iPatternClass = NULL;
            iPrecedence = aPrecedence;
            iPredicate.Set(aPredicate.Get());

            GenericClass *gen = aPredicate.Get()->Generic();
            Check(gen != NULL,KLispErrInvalidArg);
            Check(StrEqual(gen->TypeName(),"\"Pattern\""),KLispErrInvalidArg);

            iPatternClass = (PatternClass*)gen;
            iBody.Set(aBody.Get());
        }

	/// Return true if the corresponding pattern matches.
        virtual LispBoolean Matches(LispEnvironment& aEnvironment, LispPtr* aArguments);

	/// Access #iPrecedence
        virtual LispInt Precedence() const;

	/// Access #iBody
        virtual LispPtr& Body();

    protected:
	/// The precedence of this rule.
        LispInt iPrecedence;

	/// The body of this rule.
        LispPtr iBody;

	/// Generic object of type \c Pattern containing #iPatternClass
        LispPtr iPredicate;

	/// The pattern that decides whether this rule matches.
        PatternClass *iPatternClass;
    };

    /// Constructor.
    /// \param aParameters linked list constaining the names of the arguments
    ///
    /// #iParamList and #iParameters are set from \a aParameters.
    BranchingUserFunction(LispPtr& aParameters);

    /// Destructor.
    /// There is no code inside this function.
    virtual ~BranchingUserFunction();

    /// Evaluate the function on given arguments.
    /// \param aResult (on output) the result of the evaluation
    /// \param aEnvironment the underlying Lisp environment
    /// \param aArguments the arguments to the function
    ///
    /// First, all arguments are evaluated by the evaluator associated
    /// to \a aEnvironment, unless the \c iHold flag of the
    /// corresponding parameter is true. Then a new LispLocalFrame is
    /// constructed, in which the actual arguments are assigned to the
    /// names of the formal arguments, as stored in \c iParameter. Then
    /// all rules in #iRules are tried one by one. The body of the
    /// first rule that matches is evaluated, and the result is put in
    /// \a aResult. If no rule matches, \a aResult will recieve a new
    /// expression with evaluated arguments.
    virtual void Evaluate(LispPtr& aResult,LispEnvironment& aEnvironment,
                  LispPtr& aArguments);

    /// Put an argument on hold.
    /// \param aVariable name of argument to put un hold
    ///
    /// The \c iHold flag of the corresponding argument is set. This
    /// implies that this argument is not evaluated by Evaluate().
    virtual void HoldArgument(LispStringPtr aVariable);

    /// Return true if the arity of the function equals \a aArity.
    virtual LispInt IsArity(LispInt aArity) const;

    /// Return the arity (number of arguments) of the function.
    LispInt Arity() const;

    /// Add a BranchRule to the list of rules.
    /// \sa InsertRule()
    virtual void DeclareRule(LispInt aPrecedence, LispPtr& aPredicate,
                             LispPtr& aBody);

    /// Add a BranchRuleTruePredicate to the list of rules.
    /// \sa InsertRule()
    virtual void DeclareRule(LispInt aPrecedence, LispPtr& aBody);

    /// Add a BranchPattern to the list of rules.
    /// \sa InsertRule()
    void DeclarePattern(LispInt aPrecedence, LispPtr& aPredicate,
                        LispPtr& aBody);

    /// Insert any BranchRuleBase object in the list of rules.
    /// This function does the real work for DeclareRule() and
    /// DeclarePattern(): it inserts the rule in #iRules, while
    /// keeping it sorted. The algorithm is \f$O(\log n)\f$, where
    /// \f$n\f$ denotes the number of rules.
    void InsertRule(LispInt aPrecedence,BranchRuleBase* newRule);

    /// Return the argument list, stored in #iParamList
    virtual LispPtr& ArgList();

protected:
    /// List of arguments, with corresponding \c iHold property.
    CArrayGrower<BranchParameter> iParameters;

    /// List of rules, sorted on precedence.
    CDeletingArrayGrower<BranchRuleBase*>     iRules;

    /// List of arguments
    LispPtr iParamList;
};

class ListedBranchingUserFunction : public BranchingUserFunction
{
public:
    ListedBranchingUserFunction(LispPtr& aParameters);
    virtual LispInt IsArity(LispInt aArity) const;
    virtual void Evaluate(LispPtr& aResult,LispEnvironment& aEnvironment,
                  LispPtr& aArguments);
};


class MacroUserFunction : public BranchingUserFunction
{
public:
    MacroUserFunction(LispPtr& aParameters);
    virtual void Evaluate(LispPtr& aResult,LispEnvironment& aEnvironment,
                  LispPtr& aArguments);
/*
    class MacroRule : public BranchRuleBase
    {
    public:
        inline MacroRule(LispPtr& aBody) : iBody(aBody){}
        virtual LispBoolean Matches(LispEnvironment& aEnvironment, LispPtr* aArguments);
        virtual LispInt Precedence() const;
        virtual LispPtr& Body();
    private:
        LispPtr iBody;
    };
*/
};


class ListedMacroUserFunction : public MacroUserFunction
{
public:
    ListedMacroUserFunction(LispPtr& aParameters);
    virtual LispInt IsArity(LispInt aArity) const;
    virtual void Evaluate(LispPtr& aResult,LispEnvironment& aEnvironment,
                  LispPtr& aArguments);
};




#endif

