/*
 * node.cpp - part of abakus
 * Copyright (C) 2004, 2005 Michael Pyne <michael.pyne@kdemail.net>
 *
 * 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
 */
#include <kdebug.h>

#include <math.h>

#include "node.h"
#include "valuemanager.h"
#include "function.h"

void Node::deleteNode(Node *node)
{
    if(dynamic_cast<BaseFunction *>(node) != 0)
        delete node;
}

BaseFunction::BaseFunction(const char *name) :
    m_name(name)
{
}

const Function *BaseFunction::function() const
{
    return FunctionManager::instance()->function(m_name);
}

UnaryFunction::UnaryFunction(const char *name, Node *operand) :
    BaseFunction(name), m_node(operand)
{
}

UnaryFunction::~UnaryFunction()
{
    deleteNode(m_node);
    m_node = 0;
}

void UnaryFunction::setOperand(Node *operand)
{
    m_node = operand;
}

void UnaryFunction::applyMap(NodeFunctor &fn) const
{
    fn(operand());
    fn(this);
}

TQString UnaryFunction::infixString() const
{
    return TQString("%1(%2)").arg(name(), operand()->infixString());
}

BuiltinFunction::BuiltinFunction(const char *name, Node *operand) :
    UnaryFunction(name, operand)
{
}

Abakus::number_t BuiltinFunction::value() const
{
    if(function() && operand()) {
	Abakus::number_t fnValue = operand()->value();
	return evaluateFunction(function(), fnValue);
    }

    return Abakus::number_t(0);
}

Abakus::number_t BuiltinFunction::derivative() const
{
    Abakus::number_t du = operand()->derivative();
    Abakus::number_t value = operand()->value();
    Abakus::number_t one(1), zero(0);

    if(du == zero)
        return du;

    // In case these functions get added later, these derivatives may
    // be useful:
    // d/dx(asinh u) = (du/dx * 1 / sqrt(x^2 + 1))
    // d/dx(acosh u) = (du/dx * 1 / sqrt(x^2 - 1))
    // d/dx(atanh u) = (du/dx * 1 / (1 - x^2))

    // This is very unfortunate duplication.
    if(name() == "sin")
        return value.cos() * du;
    else if(name() == "cos")
        return -value.sin() * du;
    else if(name() == "tan") {
        Abakus::number_t cosResult;

        cosResult = value.cos();
        cosResult = cosResult * cosResult;
        return one / cosResult;
    }
    else if(name() == "asinh") {
        value = value * value + one;
        return du / value.sqrt();
    }
    else if(name() == "acosh") {
        value = value * value - one;
        return du / value.sqrt();
    }
    else if(name() == "atanh") {
        value = one - value * value;
        return du / value;
    }
    else if(name() == "sinh") {
        return du * value.cosh();
    }
    else if(name() == "cosh") {
        return du * value.sinh(); // Yes the sign is correct.
    }
    else if(name() == "tanh") {
        Abakus::number_t tanh = value.tanh();

        return du * (one - tanh * tanh);
    }
    else if(name() == "atan") {
        return one * du / (one + value * value);
    }
    else if(name() == "acos") {
        // Same as asin but with inverted sign.
        return -(one / (value * value - one).sqrt() * du);
    }
    else if(name() == "asin") {
        return one / (value * value - one).sqrt() * du;
    }
    else if(name() == "ln") {
        return du / value;
    }
    else if(name() == "exp") {
        return du * value.exp();
    }
    else if(name() == "log") {
        return du / value / Abakus::number_t(10).ln();
    }
    else if(name() == "sqrt") {
        Abakus::number_t half("0.5");
        return half * value.pow(-half) * du;
    }
    else if(name() == "abs") {
        return (value / value.abs()) * du;
    }

    // Approximate it.

    Abakus::number_t epsilon("1e-15");
    Abakus::number_t fxh = evaluateFunction(function(), value + epsilon);
    Abakus::number_t fx = evaluateFunction(function(), value);
    return (fxh - fx) / epsilon;
}

DerivativeFunction::~DerivativeFunction()
{
    deleteNode(m_operand);
    m_operand = 0;
}

Abakus::number_t DerivativeFunction::value() const
{
    ValueManager *vm = ValueManager::instance();
    Abakus::number_t result;

    if(vm->isValueSet("x")) {
        Abakus::number_t oldValue = vm->value("x");

        vm->setValue("x", m_where->value());
        result = m_operand->derivative();
        vm->setValue("x", oldValue);
    }
    else {
        vm->setValue("x", m_where->value());
        result = m_operand->derivative();
        vm->removeValue("x");
    }

    return result;
}

Abakus::number_t DerivativeFunction::derivative() const
{
    kdError() << k_funcinfo << endl;
    kdError() << "This function is never supposed to be called!\n";

    return m_operand->derivative();
}

void DerivativeFunction::applyMap(NodeFunctor &fn) const
{
    fn(m_operand);
    fn(this);
}

TQString DerivativeFunction::infixString() const
{
    return TQString("deriv(%1, %2)").arg(m_operand->infixString(), m_where->infixString());
}

UnaryOperator::UnaryOperator(Type type, Node *operand)
    : m_type(type), m_node(operand)
{
}

UnaryOperator::~UnaryOperator()
{
    deleteNode(m_node);
    m_node = 0;
}

void UnaryOperator::applyMap(NodeFunctor &fn) const
{
    fn(operand());
    fn(this);
}

TQString UnaryOperator::infixString() const
{
    if(dynamic_cast<BinaryOperator *>(operand()))
        return TQString("-(%1)").arg(operand()->infixString());

    return TQString("-%1").arg(operand()->infixString());
}

Abakus::number_t UnaryOperator::derivative() const
{
    switch(type()) {
	case Negation:
	    return -(operand()->derivative());

	default:
	    kdError() << "Impossible case encountered for UnaryOperator!\n";
	    return Abakus::number_t(0);
    }
}

Abakus::number_t UnaryOperator::value() const
{
    switch(type()) {
	case Negation:
	    return -(operand()->value());

	default:
	    kdError() << "Impossible case encountered for UnaryOperator!\n";
	    return Abakus::number_t(0);
    }
}

BinaryOperator::BinaryOperator(Type type, Node *left, Node *right) :
    m_type(type), m_left(left), m_right(right)
{
}

BinaryOperator::~BinaryOperator()
{
    deleteNode(m_left);
    m_left = 0;

    deleteNode(m_right);
    m_right = 0;
}

void BinaryOperator::applyMap(NodeFunctor &fn) const
{
    fn(leftNode());
    fn(rightNode());
    fn(this);
}

TQString BinaryOperator::infixString() const
{
    TQString op;

    switch(type()) {
        case Addition:
            op = "+";
        break;

        case Subtraction:
            op = "-";
        break;

        case Multiplication:
            op = "*";
        break;

        case Division:
            op = "/";
        break;

        case Exponentiation:
            op = "^";
        break;

        default:
            op = "Error";
    }
    
    TQString left = TQString(isSimpleNode(leftNode()) ? "%1" : "(%1)").arg(leftNode()->infixString());
    TQString right = TQString(isSimpleNode(rightNode()) ? "%1" : "(%1)").arg(rightNode()->infixString());

    return TQString("%1 %2 %3").arg(left, op, right);
}

Abakus::number_t BinaryOperator::derivative() const
{
    if(!leftNode() || !rightNode()) {
	kdError() << "Can't evaluate binary operator!\n";
	return Abakus::number_t(0);
    }

    Abakus::number_t f = leftNode()->value();
    Abakus::number_t fPrime = leftNode()->derivative();
    Abakus::number_t g = rightNode()->value();
    Abakus::number_t gPrime = rightNode()->derivative();

    switch(type()) {
	case Addition:
	    return fPrime + gPrime;

	case Subtraction:
	    return fPrime - gPrime;

	case Multiplication:
	    return f * gPrime + fPrime * g;

	case Division:
	    return (g * fPrime - f * gPrime) / (g * g);

	case Exponentiation:
            return f.pow(g) * ((g / f) * fPrime + gPrime * f.ln());

	default:
	    kdError() << "Impossible case encountered evaluating binary operator!\n";
	    return Abakus::number_t(0);
    }
}

Abakus::number_t BinaryOperator::value() const
{
    if(!leftNode() || !rightNode()) {
	kdError() << "Can't evaluate binary operator!\n";
	return Abakus::number_t(0);
    }

    Abakus::number_t lValue = leftNode()->value();
    Abakus::number_t rValue = rightNode()->value();

    switch(type()) {
	case Addition:
	    return lValue + rValue;

	case Subtraction:
	    return lValue - rValue;

	case Multiplication:
	    return lValue * rValue;

	case Division:
	    return lValue / rValue;

	case Exponentiation:
            return lValue.pow(rValue);

	default:
	    kdError() << "Impossible case encountered evaluating binary operator!\n";
	    return Abakus::number_t(0);
    }
}

bool BinaryOperator::isSimpleNode(Node *node) const
{
    if(dynamic_cast<Identifier *>(node) ||
       dynamic_cast<NumericValue *>(node) ||
       dynamic_cast<UnaryOperator *>(node) ||
       dynamic_cast<BaseFunction *>(node))
    {
        return true;
    }

    return false;
}

Identifier::Identifier(const char *name) : m_name(name)
{
}

Abakus::number_t Identifier::value() const
{
    return ValueManager::instance()->value(name());
}

void Identifier::applyMap(NodeFunctor &fn) const
{
    fn(this);
}

TQString NumericValue::infixString() const
{
    return value().toString();
}
