/**
    This file is part of KMathTool, a KDE program for the math class...
    Copyright (C) 2002 Dominique Devriese <fritmebufstek@pandora.be>

    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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA

*/

#ifndef DEFS_H
#define DEFS_H

class kdbgstream;
#include <vector>

using namespace std;

class FormulaNode {
public:
  typedef std::vector<FormulaNode*> vec;
  FormulaNode() { allNodes.push_back( this ); };
  virtual ~FormulaNode() {};
  // the exponent of x of this node.  If undefined, ( e.g. x^x or
  // x^(0,1) ), a node can return NoDegree.  Anything above two
  // isn't allowed anyway...
  virtual int degree() const = 0;
  virtual double value() const = 0;
  static const int NoDegree = -100;
  // try to return a node which is equivalent to you...
  // if you do, do a "delete this;" too...
  // you can return 0 if something is not allowed (e.g. x^x).
  virtual FormulaNode* simplify() = 0;
  virtual void print( kdbgstream& o ) const = 0;
  static void deleteAll()
  {
    for( vec::iterator i = allNodes.begin(); i != allNodes.end(); ++i )
      delete *i;
    allNodes.clear();
  };
protected:
  // we keep all nodes we create in this vector, so we don't need
  // reference counting and such, and so we can prevent all memory
  // leaks...
  static vec allNodes;
};

inline kdbgstream& operator<<( kdbgstream& o, const FormulaNode& n )
{
  n.print( o );
  return o;
};

class ConstantNode
  : public FormulaNode
{
public:
  ConstantNode( const double va ) : v( va ) {};
  virtual int degree() const { return 0; };
  virtual double value() const { return v; };
  double v;
  virtual FormulaNode* simplify() { return this; };
  virtual void print( kdbgstream& o ) const;
};

class VariableNode
  : public FormulaNode
{
public:
  VariableNode( const int de = 1 ) : d( de ) {};
  virtual int degree() const { return d; };
  virtual FormulaNode* simplify() { return this; };
  virtual void print( kdbgstream& o ) const;
  virtual double value() const { return 1; };
  const int d;
};

// ABC for MultiplyNode and SumNode...
class AssocNode
  : public FormulaNode
{
public:
  AssocNode( FormulaNode* a, FormulaNode* b )
  {
    fs.push_back(a);
    fs.push_back(b);
  };

  AssocNode( const vec& a )
    : fs( a )
  {
  };
  AssocNode( FormulaNode* a, const vec& b )
    : fs( b )
  {
    fs.push_back( a );
  };
  ~AssocNode()
  {
  };
  vec fs;
};

class MultiplyNode
  : public AssocNode
{
public:
  MultiplyNode( FormulaNode* a, FormulaNode* b ) : AssocNode( a, b ) {};
  MultiplyNode( const vec& v ) : AssocNode( v ) {};
  MultiplyNode( FormulaNode* a, const vec& b ) : AssocNode( a, b ) {};
  virtual int degree() const;
  virtual FormulaNode* simplify();
  virtual void print( kdbgstream& o ) const;
  virtual double value() const;
};

class SumNode
  : public AssocNode
{
public:
  SumNode( FormulaNode* a, FormulaNode* b ) : AssocNode( a, b ) {};
  SumNode( const vec& a ) : AssocNode( a ) {};
  SumNode( FormulaNode* a, const vec& b ) : AssocNode( a, b ) {};
  virtual int degree() const;
  virtual FormulaNode* simplify();
  virtual void print( kdbgstream& o ) const;
  virtual double value() const;
};

class PowerNode
  : public FormulaNode
{
public:
  PowerNode( FormulaNode* a, FormulaNode* b ) : f1( a ), f2( b ) {};
  ~PowerNode() {};
  virtual int degree() const;
  virtual FormulaNode* simplify();
  virtual void print( kdbgstream& o ) const;
  virtual double value() const;
  FormulaNode* f1;
  FormulaNode* f2;
};

#endif
