diff options
Diffstat (limited to 'src/function.cpp')
-rw-r--r-- | src/function.cpp | 298 |
1 files changed, 298 insertions, 0 deletions
diff --git a/src/function.cpp b/src/function.cpp new file mode 100644 index 0000000..70c4f1c --- /dev/null +++ b/src/function.cpp @@ -0,0 +1,298 @@ +/* + * function.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 "numerictypes.h" + +#include <kdebug.h> + +#include <qvaluevector.h> +#include <qstring.h> +#include <qregexp.h> + +#include <math.h> + +#include "function.h" +#include "node.h" +#include "valuemanager.h" +#include "hmath.h" + +// Used to try and avoid recursive function definitions +class DupFinder : public NodeFunctor +{ + public: + DupFinder(const QString &nameToFind) : + m_name(nameToFind), m_valid(true) + { + } + + virtual ~DupFinder() { } + + bool isValid() const { return m_valid; } + + virtual void operator()(const Node *node) + { + if(!m_valid) + return; + + const BaseFunction *fn = dynamic_cast<const BaseFunction *>(node); + if(fn && fn->name() == m_name) + m_valid = false; // Duplicate detected + } + + private: + QString m_name; + bool m_valid; +}; + +// Define static member for FunctionManager +FunctionManager *FunctionManager::m_manager = 0; + +FunctionManager *FunctionManager::instance() +{ + if(!m_manager) + m_manager = new FunctionManager; + + return m_manager; +} + +FunctionManager::FunctionManager(QObject *parent, const char *name) : + QObject(parent, name) +{ + m_dict.setAutoDelete(true); +} + +// Dummy return value to enable static initialization in the DECL_*() +// macros. +bool FunctionManager::addFunction(const QString &name, function_t fn, const QString &desc) +{ + Function *newFn = new Function; + QRegExp returnTrigRE("^a(cos|sin|tan)"); + QRegExp needsTrigRE("^(cos|sin|tan)"); + QString fnName(name); + + newFn->name = name; + newFn->description = desc; + newFn->fn = fn; + newFn->userDefined = false; + newFn->returnsTrig = fnName.contains(returnTrigRE); + newFn->needsTrig = fnName.contains(needsTrigRE); + + m_dict.insert(name, newFn); + + return false; +} + +#define DECLARE_FUNC(name, fn, desc) bool dummy##name = FunctionManager::instance()->addFunction(#name, fn, desc) + +// Declares a function name that is implemented by the function of a different +// name. e.g. atan -> Abakus::number_t::arctan() +#define DECLARE_FUNC2(name, fnName, desc) DECLARE_FUNC(name, &Abakus::number_t::fnName, desc) + +// Declares a function name that is implemented by the function of the +// same base name. +#define DECLARE_FUNC1(name, desc) DECLARE_FUNC2(name, name, desc) + +DECLARE_FUNC1(sin, "Trigonometric sine"); +DECLARE_FUNC1(cos, "Trigonometric cosine"); +DECLARE_FUNC1(tan, "Trigonometric tangent"); + +DECLARE_FUNC1(sinh, "Hyperbolic sine"); +DECLARE_FUNC1(cosh, "Hyperbolic cosine"); +DECLARE_FUNC1(tanh, "Hyperbolic tangent"); + +DECLARE_FUNC1(atan, "Inverse tangent"); +DECLARE_FUNC1(acos, "Inverse cosine"); +DECLARE_FUNC1(asin, "Inverse sine"); + +DECLARE_FUNC1(asinh, "Inverse hyperbolic sine"); +DECLARE_FUNC1(acosh, "Inverse hyperbolic cosine"); +DECLARE_FUNC1(atanh, "Inverse hyperbolic tangent"); + +DECLARE_FUNC1(abs, "Absolute value of number"); +DECLARE_FUNC1(sqrt, "Square root"); +DECLARE_FUNC1(ln, "Natural logarithm (base e)"); +DECLARE_FUNC1(log, "Logarithm (base 10)"); +DECLARE_FUNC1(exp, "Natural exponential function"); + +DECLARE_FUNC1(round, "Round to nearest number"); +DECLARE_FUNC1(ceil, "Nearest greatest integer"); +DECLARE_FUNC1(floor, "Nearest lesser integer"); +DECLARE_FUNC2(int, integer, "Integral part of number"); +DECLARE_FUNC1(frac, "Fractional part of number"); + +Function *FunctionManager::function(const QString &name) +{ + return m_dict[name]; +} + +// Returns true if the named identifier is a function, false otherwise. +bool FunctionManager::isFunction(const QString &name) +{ + return function(name) != 0; +} + +bool FunctionManager::isFunctionUserDefined(const QString &name) +{ + const Function *fn = function(name); + return (fn != 0) && (fn->userDefined); +} + +bool FunctionManager::addFunction(BaseFunction *fn, const QString &dependantVar) +{ + // First see if this function is recursive + DupFinder dupFinder(fn->name()); + UnaryFunction *unFunction = dynamic_cast<UnaryFunction *>(fn); + if(unFunction && unFunction->operand()) { + unFunction->operand()->applyMap(dupFinder); + if(!dupFinder.isValid()) + return false; + } + + // Structure holds extra data needed to call the user defined + // function. + UserFunction *newFn = new UserFunction; + newFn->sequenceNumber = m_dict.count(); + newFn->fn = fn; + newFn->varName = QString(dependantVar); + + // Now setup the Function data structure that holds the information + // we need to access and call the function later. + Function *fnTabEntry = new Function; + fnTabEntry->name = fn->name(); + fnTabEntry->userFn = newFn; + fnTabEntry->returnsTrig = false; + fnTabEntry->needsTrig = false; + fnTabEntry->userDefined = true; + + if(m_dict.find(fn->name())) + emit signalFunctionRemoved(fn->name()); + + m_dict.replace(fn->name(), fnTabEntry); + emit signalFunctionAdded(fn->name()); + + return true; +} + +void FunctionManager::removeFunction(const QString &name) +{ + Function *fn = function(name); + + // If we remove a function, we need to decrement the sequenceNumber of + // functions after this one. + if(fn && fn->userDefined) { + int savedSeqNum = fn->userFn->sequenceNumber; + + // Emit before we actually remove it so that the info on the function + // can still be looked up. + emit signalFunctionRemoved(name); + + delete fn->userFn; + fn->userFn = 0; + m_dict.remove(name); + + QDictIterator<Function> it(m_dict); + for (; it.current(); ++it) { + UserFunction *userFn = it.current()->userDefined ? it.current()->userFn : 0; + if(userFn && userFn->sequenceNumber > savedSeqNum) + --it.current()->userFn->sequenceNumber; + } + } +} + +QStringList FunctionManager::functionList(FunctionManager::FunctionType type) +{ + QDictIterator<Function> it(m_dict); + QStringList functions; + + switch(type) { + case Builtin: + for(; it.current(); ++it) + if(!it.current()->userDefined) + functions += it.current()->name; + break; + + case UserDefined: + // We want to return the function names in the order they were + // added. + { + QValueVector<Function *> fnTable(m_dict.count(), 0); + QValueVector<int> sequenceNumberTable(m_dict.count(), -1); + + // First find out what sequence numbers we have. + for(; it.current(); ++it) + if(it.current()->userDefined) { + int id = it.current()->userFn->sequenceNumber; + fnTable[id] = it.current(); + sequenceNumberTable.append(id); + } + + // Now sort the sequence numbers and return the ordered list + qHeapSort(sequenceNumberTable.begin(), sequenceNumberTable.end()); + + for(unsigned i = 0; i < sequenceNumberTable.count(); ++i) + if(sequenceNumberTable[i] >= 0) + functions += fnTable[sequenceNumberTable[i]]->name; + } + break; + + case All: + functions += functionList(Builtin); + functions += functionList(UserDefined); + break; + } + + return functions; +} + +// Applies the function identified by func, using value as a parameter. +Abakus::number_t evaluateFunction(const Function *func, const Abakus::number_t value) +{ + if(func->userDefined) { + // Pull real entry from userFunctionTable + UserFunction *realFunction = func->userFn; + + bool wasSet = ValueManager::instance()->isValueSet(realFunction->varName); + Abakus::number_t oldValue; + if(wasSet) + oldValue = ValueManager::instance()->value(realFunction->varName); + + ValueManager::instance()->setValue(realFunction->varName, value); + Abakus::number_t result = realFunction->fn->value(); + + if(wasSet) + ValueManager::instance()->setValue(realFunction->varName, oldValue); + else + ValueManager::instance()->removeValue(realFunction->varName); + + return result; + } + + return (value.*(func->fn))(); +} + +void setTrigMode(Abakus::TrigMode mode) +{ + Abakus::m_trigMode = mode; +} + +Abakus::TrigMode trigMode() +{ + return Abakus::m_trigMode; +} + +#include "function.moc" |