diff options
Diffstat (limited to 'src/node.cpp')
-rw-r--r-- | src/node.cpp | 419 |
1 files changed, 419 insertions, 0 deletions
diff --git a/src/node.cpp b/src/node.cpp new file mode 100644 index 0000000..bb49676 --- /dev/null +++ b/src/node.cpp @@ -0,0 +1,419 @@ +/* + * node.cpp - part of abakus + * Copyright (C) 2004, 2005 Michael Pyne <[email protected]> + * + * 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 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); +} + +QString UnaryFunction::infixString() const +{ + return QString("%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); +} + +QString DerivativeFunction::infixString() const +{ + return QString("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); +} + +QString UnaryOperator::infixString() const +{ + if(dynamic_cast<BinaryOperator *>(operand())) + return QString("-(%1)").arg(operand()->infixString()); + + return QString("-%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); +} + +QString BinaryOperator::infixString() const +{ + QString 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"; + } + + QString left = QString(isSimpleNode(leftNode()) ? "%1" : "(%1)").arg(leftNode()->infixString()); + QString right = QString(isSimpleNode(rightNode()) ? "%1" : "(%1)").arg(rightNode()->infixString()); + + return QString("%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); +} + +QString NumericValue::infixString() const +{ + return value().toString(); +} + +// vim: set et ts=8 sw=4: |