/**
    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
*/
#include "nodes.h"

#include <algorithm>

#include <math.h>

#include <kdebug.h>

using std::back_insert_iterator;

FormulaNode::vec FormulaNode::allNodes;

struct DegreeComp {
  bool operator()( const FormulaNode* a, const FormulaNode* b )
  {
    return a->degree() < b->degree();
  };
};

int MultiplyNode::degree() const
{
  for( vec::const_iterator i = fs.begin(); i != fs.end(); ++i )
    {
      if( (*i)->degree() == 0 && (*i)->value() == 0 ) return 0;
    };
  int t = 0;
  for( vec::const_iterator i = fs.begin(); i != fs.end(); ++i )
    {
      t += (*i)->degree();
    };
  return t;
}

int SumNode::degree() const
{
  int t = 0;
  for( vec::const_iterator i = fs.begin(); i != fs.end(); ++i )
    {
      t = max( t, (*i)->degree() );
    };
  return t;
}

int PowerNode::degree() const
{
  if( ( f1 )->degree() == 0 && ( f1 )->value() == 0 || ( f1 )->value() == 1 )
    return 0;
  if( f2->degree() != 0 ) return NoDegree;
  // f2->value() should be an integer (not 0,100 and stuff like that...)
  if( static_cast<double>(static_cast<int>(f2->value())) != f2->value() )
    return NoDegree;
  else return ( f1 )->degree() * static_cast<int>(f2->value());
}

FormulaNode* MultiplyNode::simplify()
{
  for( vec::iterator i = fs.begin(); i != fs.end(); ++i )
    {
      (*i) = (*i)->simplify();
      if( ! *i )
	{
	  return 0;
	};
      if( (*i)->degree() == 0 )
	{
	  // some special cases:
	  if( (*i)->value() == 0 )
	    {
	      // x*y*0 == 0
	      return new ConstantNode( 0 );
	    }
	  if( (*i)->value() == 1 )
	    {
	      // x*y*1 == x*y
	      fs.erase( i );
	      // we return here to avoid problems with changing a
	      // pointer while iterating over pointers to the same
	      // vector...
	      return this->simplify();
	    };
	};
      MultiplyNode* m = 0;
      if( ( m = dynamic_cast<MultiplyNode*>( *i ) ) )
	{
	  fs.erase( i );
	  // we incorporate the other MultiplyNode, to flatten the
	  // hierarchy...
	  std::copy( m->fs.begin(),
		     m->fs.end(),
		     back_insert_iterator<vec>( fs )
		     );
	  return this->simplify();
	};

      SumNode* s = 0;
      if( ( s = dynamic_cast<SumNode*>( *i ) ) )
	{
	  // "distributivity" of * with +
	  fs.erase( i );
	  vec v;
	  for( vec::iterator i = s->fs.begin();
	       i != s->fs.end();
	       ++i )
	    {
	      v.push_back( new MultiplyNode( *i, fs ) );
	    };
	  fs.push_back( s );
	  SumNode* r = new SumNode( v );
	  return r->simplify();
	};
    };
  if( fs.size() == 1 )
    {
      // we are a multiplynode of one element -> just return the
      // element...
      return fs[0];
    };
  if( fs.size() == 0 )
    {
      // we're empty --> the identity element 1...
      return new ConstantNode( 1 );
    };
  // we sort our data by degree, so we can easily see if there are two
  // nodes of degree 0...
  sort( fs.begin(), fs.end(), DegreeComp() );
  for( vec::iterator i = fs.begin() + 1; i != fs.end(); ++i )
    {
      if( (*i)->degree() == 0 && (*(i-1))->degree() == 0 )
	{
	  ConstantNode* n = new ConstantNode( (*i)->value() * (*(i-1))->value() );
	  fs.erase(i);
	  fs.erase(i-1);
	  fs.push_back( n );
	  return this->simplify();
	};
      if( (*i)->degree() != 0 && (*(i-1))->degree() != 0 )
	{
	  // these should be VariableNodes, since the other options
	  // should have already been handled...
	  assert( dynamic_cast<VariableNode*>( *i )
		  && dynamic_cast<VariableNode*>( *(i-1) ) );
	  VariableNode* n = new VariableNode( (*i)->degree() +
					      (*(i-1))->degree() );
	  fs.erase(i);
	  fs.erase(i-1);
	  fs.push_back( n );
	  return this->simplify();
	};
    };
  return this;
}

FormulaNode* SumNode::simplify()
{
  for( vec::iterator i = fs.begin(); i != fs.end(); ++i )
    {
      (*i) = (*i)->simplify();
      if( ! *i ) return 0;
      if( (*i)->degree() == 0 && (*i)->value() == 0 )
	{
	  // x+y+0 == x+y
	  fs.erase( i );
	  // we return here to avoid problems with changing a
	  // pointer while iterating over pointers to the same
	  // vector...
	  return this->simplify();
	};
      SumNode* n = 0;
      if( ( n = dynamic_cast<SumNode*>( * i ) ) )
	{
	  fs.erase( i );
	  // we incorporate the other SumNode, to flatten the
	  // hierarchy...
	  std::copy( n->fs.begin(), n->fs.end(), back_insert_iterator<vec>( fs ) );
	  return this->simplify();
	};
    };
  if( fs.size() == 1 )
    {
      // we are a sumnode of one element -> just return the
      // element...
      return fs[0];
    };
  if( fs.size() == 0 )
    {
      // we're empty --> the identity element 0...
      ConstantNode* s = new ConstantNode( 0 );
      return s;
    };
  // we sort our data by degree
  sort( fs.begin(), fs.end(), DegreeComp() );
  for( vec::iterator i = fs.begin() + 1; i != fs.end(); ++i )
    {
      if( (*i)->degree() == 0 && (*(i-1))->degree() == 0 )
	{
	  ConstantNode* n = new ConstantNode( (*i)->value() + (*(i-1))->value() );
	  fs.erase(i);
	  fs.erase(i-1);
	  fs.push_back( n );
	  return this->simplify();
	};
      if( (*i)->degree() == (*(i-1))->degree() )
	{
	  VariableNode* n = new VariableNode( (*i)->degree() );
	  ConstantNode* c = new ConstantNode( (*i)->value() + (*(i-1))->value() );

	  MultiplyNode* m = new MultiplyNode( c, n );
	  fs.erase(i);
	  fs.erase(i-1);
	  fs.push_back( m );
	  return this->simplify();
	};
    };
  return this;
};

void ConstantNode::print( kdbgstream& o ) const
{
  o << v;
}
void VariableNode::print( kdbgstream& o ) const
{
  o << "x" << "^" << d;
}

void MultiplyNode::print( kdbgstream& o ) const
{
  for( vec::const_iterator i = fs.begin(); i != fs.end(); ++i )
    {
      if( i != fs.begin() ) o << "*";
      o << "(" << **i << ")";
    };
}

void SumNode::print( kdbgstream& o ) const
{
  for( vec::const_iterator i = fs.begin(); i != fs.end(); ++i )
    {
      if( i != fs.begin() ) o << "+";
      o << "(" << **i << ")";
    };
}
void PowerNode::print( kdbgstream& o ) const
{
  o << "(" << *( f1 ) << ")^(" << *f2 << ")";
}

FormulaNode* PowerNode::simplify()
{
  ( f1 ) = ( f1 )->simplify();
  if( !( f1 ) ) return 0;
  f2 = f2->simplify();
  if( !f2 ) return 0;
  if( ( f1 )->degree() == 0 && ( f1 )->value() == 0 )
    return new ConstantNode( 0 );
  if( ( f1 )->degree() == 0 && ( f1 )->value() == 1 )
    return new ConstantNode( 1 );
  if( f2->degree() == 0 && f2->value() == 0 )
    return new ConstantNode( 1 );

  // no variable exponents...
  if( f2->degree() != 0 ) return 0;

  VariableNode* n;
  if( ( n = dynamic_cast<VariableNode*>( f1 ) ) )
    {
      double v = f2->value();
      if( static_cast<double>(static_cast<int>(v)) != v ) return 0;
      return new VariableNode( static_cast<int>( f2->value() * n->degree() ) );
    };

  MultiplyNode* m;
  if( ( m = dynamic_cast<MultiplyNode*>( f1 ) ) )
    {
      AssocNode::vec v;
      for( AssocNode::vec::iterator i = m->fs.begin(); i != m->fs.end(); ++i )
	{
	  v.push_back( new PowerNode( *i, f2 ) );
	};
      MultiplyNode* n = new MultiplyNode( v );
      return n->simplify();
    };
  SumNode* s = 0;
  if( ( s = dynamic_cast<SumNode*>( ( f1 ) ) ) )
    {
      int m;
      if( f2->degree() != 0 || ( static_cast<double>( m = static_cast<int>( f2->value() )) != f2->value() ) ) return 0;
      AssocNode::vec v;
      for( int i = 0; i != m; ++i )
	{
	  v.push_back( s );
	};
      MultiplyNode* u = new MultiplyNode( v );
      return u;
    };
  return this;
}

double MultiplyNode::value() const
{
  double tmp = 1;
  for( vec::const_iterator i = fs.begin(); i != fs.end(); ++i )
    {
      tmp *= (*i)->value();
    };
  return tmp;
}

double SumNode::value() const
{
  double tmp = 0;
  for( vec::const_iterator i = fs.begin(); i != fs.end(); ++i )
    {
      tmp += (*i)->value();
    };
  return tmp;
}

double PowerNode::value() const
{
  return pow( ( f1 )->value(), f2->value() );
}
