AbaKus – a complex calculator
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

419 lines
9.5 KiB

/*
* 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();
}
// vim: set et ts=8 sw=4: