summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/SConscript81
-rw-r--r--src/abakus.cpp74
-rw-r--r--src/abakus.desktop38
-rw-r--r--src/abakuscommon.h22
-rw-r--r--src/abakuslistview.cpp232
-rw-r--r--src/abakuslistview.h147
-rw-r--r--src/abakusui.rc35
-rw-r--r--src/dcopIface.h47
-rw-r--r--src/dragsupport.cpp87
-rw-r--r--src/dragsupport.h33
-rw-r--r--src/editor.cpp892
-rw-r--r--src/editor.h131
-rw-r--r--src/evaluator.cpp261
-rw-r--r--src/evaluator.h131
-rw-r--r--src/function.cpp298
-rw-r--r--src/function.h122
-rw-r--r--src/hi64-app-abakus.pngbin0 -> 7079 bytes
-rw-r--r--src/hmath.cpp1797
-rw-r--r--src/hmath.h359
-rw-r--r--src/lexer.ll223
-rw-r--r--src/mainwindow.cpp825
-rw-r--r--src/mainwindow.h135
-rw-r--r--src/node.cpp419
-rw-r--r--src/node.h219
-rw-r--r--src/number.c1809
-rw-r--r--src/number.h169
-rw-r--r--src/numerictypes.cpp205
-rw-r--r--src/numerictypes.h693
-rw-r--r--src/parser.yy386
-rw-r--r--src/result.cpp33
-rw-r--r--src/result.h77
-rw-r--r--src/resultlistview.cpp149
-rw-r--r--src/resultlistview.h64
-rw-r--r--src/resultlistviewtext.cpp135
-rw-r--r--src/resultlistviewtext.h70
-rw-r--r--src/rpnmuncher.cpp267
-rw-r--r--src/rpnmuncher.h44
-rw-r--r--src/sharedptr.h122
-rw-r--r--src/valuemanager.cpp105
-rw-r--r--src/valuemanager.h69
40 files changed, 11005 insertions, 0 deletions
diff --git a/src/SConscript b/src/SConscript
new file mode 100644
index 0000000..66e701d
--- /dev/null
+++ b/src/SConscript
@@ -0,0 +1,81 @@
+#! /usr/bin/env python
+## This script demonstrates how to build and install
+## a simple kde program having KconfigXT settings
+## with scons
+##
+## Thomas Nagy, 2004, 2005
+
+## This file can be reused freely for any project (see COPYING)
+
+############################
+## load the config
+
+## Use the environment and the tools set in the top-level
+## SConstruct file (set with 'Export') - this is very important
+
+Import( 'env' )
+myenv=env.Copy()
+
+#############################
+## the programs to build
+
+# The sources for our program - only .ui, .skel and .cpp are accepted
+abakus_sources = """
+abakus.cpp
+abakuslistview.cpp
+dragsupport.cpp
+editor.cpp
+evaluator.cpp
+function.cpp
+lexer_lex.cpp
+mainwindow.cpp
+node.cpp
+numerictypes.cpp
+parser_yacc.cpp
+result.cpp
+resultlistview.cpp
+resultlistviewtext.cpp
+rpnmuncher.cpp
+valuemanager.cpp
+dcopIface.skel
+"""
+
+if myenv.get('mpfr', 'no') == 'yes':
+ myenv.Append(LIBS = ['mpfr', 'gmp'])
+else:
+ abakus_sources = abakus_sources + " hmath.cpp number.c"
+
+myenv.KDEprogram( "abakus", abakus_sources )
+myenv.KDEicon( 'abakus' )
+
+# Mark these as being created by flex and bison if it's installed.
+if myenv.Dictionary().has_key('PARSER_INCLUDED'):
+ myenv.CXXFile( "lexer_lex.cpp", "lexer.ll" )
+ myenv.CXXFile( "parser_yacc.cpp", "parser.yy", YACCFLAGS="-d" )
+
+if myenv['HAVE_ASNEEDED']:
+ myenv.Append(LINKFLAGS = '-Wl,--as-needed')
+
+myenv.Append(CXXFLAGS = '-Wno-non-virtual-dtor')
+
+############################
+## Customization
+
+## Additional include paths for compiling the source files
+## Always add '../' (top-level directory) because moc makes code that needs it
+myenv.KDEaddpaths_includes('#/src/ #/')
+
+## Necessary libraries to link against
+myenv.KDEaddlibs( 'qt-mt kio kdecore kdeprint kdeui' )
+
+#############################
+## Data to install
+
+## The ui.rc file and the tips go into datadir/appname/
+myenv.KDEinstall( 'KDEDATA', 'abakus', 'abakusui.rc' )
+
+## Warning : there is a difference between the normal destop file used for the menu
+## and the servicetype desktop file, so they go in different directories
+## you will find more information in 'test3'
+myenv.KDEinstall( 'KDEMENU', 'Utilities', 'abakus.desktop')
+
diff --git a/src/abakus.cpp b/src/abakus.cpp
new file mode 100644
index 0000000..6200641
--- /dev/null
+++ b/src/abakus.cpp
@@ -0,0 +1,74 @@
+/*
+ * abakus.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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+#include <kaboutdata.h>
+#include <kapplication.h>
+#include <kcmdlineargs.h>
+#include <kdebug.h>
+
+#include <config.h>
+
+#if HAVE_MPFR
+#include <mpfr.h>
+#endif
+
+#include "mainwindow.h"
+
+const char *const version = "0.91";
+
+int main(int argc, char **argv)
+{
+ KAboutData *about = new KAboutData("abakus", I18N_NOOP("abakus"), version,
+ I18N_NOOP("A simple keyboard-driven calculator"), KAboutData::License_GPL,
+ "(c) 2004, 2005 Michael Pyne", 0 /* text */, "http://grammarian.homelinux.net/abakus/",
+ "michael.pyne@kdemail.net");
+
+ about->addAuthor("Michael Pyne",
+ I18N_NOOP("Developer"),
+ "michael.pyne@kdemail.net",
+ "http://grammarian.homelinux.net/");
+ about->addCredit("Ariya Hidayat",
+ I18N_NOOP("High precision math routines, and inspiration for the new design came from his C++ implementation (SpeedCrunch)"),
+ "ariya@kde.org",
+ "http://speedcrunch.berlios.de/");
+ about->addCredit("Roberto Alsina",
+ I18N_NOOP("Came up with the initial idea, along with a Python implementation."),
+ "ralsina@netline.com.ar",
+ "http://dot.kde.org/1096309744/");
+ about->addCredit("Zack Rusin",
+ I18N_NOOP("Inspiration/code for the initial design came from his Ruby implementation."),
+ "zack@kde.org");
+
+#if HAVE_MPFR
+ mpfr_set_default_prec(6 * 78); // 78 digits, figure about 6 bits needed.
+ kdDebug() << "Using the MPFR high-precision library.\n";
+#else
+ kdDebug() << "Using the internal high-precision library.\n";
+#endif
+
+ KCmdLineArgs::init(argc, argv, about);
+ KApplication app;
+ MainWindow *win = new MainWindow;
+
+ app.setMainWidget(win);
+ app.connect(&app, SIGNAL(lastWindowClosed()), SLOT(quit()));
+ win->show();
+ win->resize(500, 300);
+
+ return app.exec();
+}
diff --git a/src/abakus.desktop b/src/abakus.desktop
new file mode 100644
index 0000000..282bc4e
--- /dev/null
+++ b/src/abakus.desktop
@@ -0,0 +1,38 @@
+# KDE Config File
+[Desktop Entry]
+Encoding=UTF-8
+Type=Application
+X-KDE-StartupNotify=true
+Exec=abakus -caption "%c" %i %m
+Icon=abakus
+Comment=
+Comment[xx]=xxxx
+Terminal=0
+Name=Abakus
+Name[tr]=Abaküs
+Name[xx]=xxAbakusxx
+GenericName=Calculator
+GenericName[bg]=Калкулатор
+GenericName[br]=Jederez
+GenericName[cs]=Kalkulátor
+GenericName[cy]=Cyfrifiannell
+GenericName[da]=Lommeregner
+GenericName[el]=Υπολογιστής Τσέπης
+GenericName[es]=Calculadora
+GenericName[et]=Kalkulaator
+GenericName[fr]=Calculateur
+GenericName[ga]=Áireamhán
+GenericName[gl]=Calculadora
+GenericName[it]=Calcolatrice
+GenericName[ka]=კალკულატორი
+GenericName[lt]=Skaičiuotuvas
+GenericName[nl]=Rekenmachine
+GenericName[pt]=Calculadora
+GenericName[rw]=Mubazi
+GenericName[sk]=Kalkulačka
+GenericName[sr]=Рачунаљка
+GenericName[sr@Latn]=Računaljka
+GenericName[sv]=Miniräknare
+GenericName[tr]=Hesap Makinesi
+GenericName[xx]=xxCalculatorxx
+Categories=Qt;KDE;Utility
diff --git a/src/abakuscommon.h b/src/abakuscommon.h
new file mode 100644
index 0000000..83a71f2
--- /dev/null
+++ b/src/abakuscommon.h
@@ -0,0 +1,22 @@
+// header file for pch support.
+#if defined __cplusplus
+
+#include <kapplication.h>
+#include <kdebug.h>
+#include <kpushbutton.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <klocale.h>
+#include <kcombobox.h>
+#include <kpopupmenu.h>
+#include <kaction.h>
+
+#include <qlabel.h>
+#include <qregexp.h>
+#include <qtimer.h>
+
+#include "function.h"
+#include "node.h"
+#include "valuemanager.h"
+
+#endif
diff --git a/src/abakuslistview.cpp b/src/abakuslistview.cpp
new file mode 100644
index 0000000..964c8a7
--- /dev/null
+++ b/src/abakuslistview.cpp
@@ -0,0 +1,232 @@
+/*
+ * abakuslistview.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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+#include <klocale.h>
+#include <kpopupmenu.h>
+#include <kdebug.h>
+
+#include <qdragobject.h>
+#include <qcursor.h>
+#include <qheader.h>
+
+#include "dragsupport.h"
+#include "abakuslistview.h"
+#include "valuemanager.h"
+#include "function.h"
+
+ListView::ListView(QWidget *parent, const char *name) :
+ KListView(parent, name), m_menu(0), m_usePopup(false), m_removeSingleId(0),
+ m_removeAllId(0)
+{
+ setResizeMode(LastColumn);
+ setDragEnabled(true);
+
+ connect(this, SIGNAL(contextMenuRequested(QListViewItem *, const QPoint &, int)),
+ SLOT(rightClicked(QListViewItem *, const QPoint &)));
+}
+
+QDragObject *ListView::dragObject()
+{
+ QPoint viewportPos = viewport()->mapFromGlobal(QCursor::pos());
+ QListViewItem *item = itemAt(viewportPos);
+
+ if(!item)
+ return 0;
+
+ int column = header()->sectionAt(viewportPos.x());
+ QString dragText = item->text(column);
+
+ QDragObject *drag = new QTextDrag(dragText, this, "list item drag");
+ drag->setPixmap(DragSupport::makePixmap(dragText, font()));
+
+ return drag;
+}
+
+void ListView::enablePopupHandler(bool enable)
+{
+ if(enable == m_usePopup)
+ return;
+
+ m_usePopup = enable;
+
+ if(m_usePopup) {
+ if(m_menu)
+ kdError() << "ListView menu shouldn't exist here!\n";
+
+ m_menu = new KPopupMenu(this);
+
+ m_removeSingleId = m_menu->insertItem(removeItemString(), this, SLOT(removeSelected()));
+ m_removeAllId = m_menu->insertItem("Placeholder", this, SLOT(removeAllItems()));
+ }
+ else {
+ delete m_menu;
+ m_menu = 0;
+ }
+}
+
+QString ListView::removeItemString() const
+{
+ return QString();
+}
+
+QString ListView::removeAllItemsString(unsigned count) const
+{
+ Q_UNUSED(count);
+
+ return QString();
+}
+
+void ListView::removeSelectedItem(QListViewItem *item)
+{
+ Q_UNUSED(item);
+}
+
+void ListView::removeAllItems()
+{
+}
+
+bool ListView::isItemRemovable(QListViewItem *item) const
+{
+ Q_UNUSED(item);
+
+ return false;
+}
+
+void ListView::rightClicked(QListViewItem *item, const QPoint &pt)
+{
+ if(!m_usePopup)
+ return;
+
+ m_menu->setItemEnabled(m_removeSingleId, item && isItemRemovable(item));
+ m_menu->changeItem(m_removeAllId, removeAllItemsString(childCount()));
+ m_menu->popup(pt);
+}
+
+void ListView::removeSelected()
+{
+ removeSelectedItem(selectedItem());
+}
+
+ValueListViewItem::ValueListViewItem(QListView *listView, const QString &name,
+ const Abakus::number_t &value) :
+ KListViewItem(listView, name), m_value(value)
+{
+ valueChanged();
+}
+
+void ValueListViewItem::valueChanged()
+{
+ setText(1, m_value.toString());
+ repaint();
+}
+
+void ValueListViewItem::valueChanged(const Abakus::number_t &newValue)
+{
+ m_value = newValue;
+
+ valueChanged();
+}
+
+Abakus::number_t ValueListViewItem::itemValue() const
+{
+ return m_value;
+}
+
+VariableListView::VariableListView(QWidget *parent, const char *name) :
+ ListView(parent, name)
+{
+ enablePopupHandler(true);
+}
+
+QString VariableListView::removeItemString() const
+{
+ return i18n("Remove selected variable");
+}
+
+QString VariableListView::removeAllItemsString(unsigned count) const
+{
+ // count is unreliable because not all of the elements in the list view
+ // can be removed.
+ count = 0;
+ QStringList values = ValueManager::instance()->valueNames();
+
+ for(QStringList::ConstIterator value = values.constBegin(); value != values.constEnd(); ++value)
+ if(!ValueManager::instance()->isValueReadOnly(*value))
+ ++count;
+
+ return i18n("Remove all variables (1 variable)",
+ "Remove all variables (%n variables)",
+ count);
+}
+
+bool VariableListView::isItemRemovable(QListViewItem *item) const
+{
+ return !ValueManager::instance()->isValueReadOnly(item->text(0));
+}
+
+void VariableListView::removeSelectedItem(QListViewItem *item)
+{
+ ValueManager::instance()->removeValue(item->text(0));
+}
+
+void VariableListView::removeAllItems()
+{
+ ValueManager::instance()->slotRemoveUserVariables();
+}
+
+FunctionListView::FunctionListView(QWidget *parent, const char *name) :
+ ListView(parent, name)
+{
+ enablePopupHandler(true);
+}
+
+QString FunctionListView::removeItemString() const
+{
+ return i18n("Remove selected function");
+}
+
+QString FunctionListView::removeAllItemsString(unsigned count) const
+{
+ return i18n("Remove all functions (1 function)",
+ "Remove all functions (%n functions)",
+ count);
+}
+
+bool FunctionListView::isItemRemovable(QListViewItem *item) const
+{
+ return true;
+}
+
+void FunctionListView::removeSelectedItem(QListViewItem *item)
+{
+ // Use section to get the beginning of the string up to (and not
+ // including) the first (
+ QString name = item->text(0).section('(', 0, 0);
+
+ FunctionManager::instance()->removeFunction(name);
+}
+
+void FunctionListView::removeAllItems()
+{
+ QStringList fns = FunctionManager::instance()->functionList(FunctionManager::UserDefined);
+
+ for(QStringList::ConstIterator fn = fns.constBegin(); fn != fns.constEnd(); ++fn)
+ FunctionManager::instance()->removeFunction(*fn);
+}
+
+#include "abakuslistview.moc"
diff --git a/src/abakuslistview.h b/src/abakuslistview.h
new file mode 100644
index 0000000..1712b6e
--- /dev/null
+++ b/src/abakuslistview.h
@@ -0,0 +1,147 @@
+#ifndef ABAKUS_LISTVIEW_H
+#define ABAKUS_LISTVIEW_H
+/*
+ * abakuslistview.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include <klistview.h>
+
+#include "numerictypes.h"
+
+class KPopupMenu;
+
+class ListView : public KListView
+{
+ Q_OBJECT
+
+ public:
+ ListView(QWidget *parent, const char *name = 0);
+
+ protected:
+ virtual QDragObject *dragObject();
+
+ /**
+ * Used to enable fancy popup handling support in subclasses. Subclasses
+ * also need to reimplement a few functions if they want to use this.
+ *
+ * This should be called in the subclass's constructor.
+ */
+ void enablePopupHandler(bool enable);
+
+ /**
+ * If using the popup menu handling, the subclass needs to return a
+ * translated string of the form "Remove selected <itemtype>".
+ */
+ virtual QString removeItemString() const;
+
+ /**
+ * If using the popup menu handling, the subclass needs to return a
+ * translated string of the form "Remove all <itemtype>s." I recommend
+ * also appending a " (%n <itemtype>s), which you can use the @p count
+ * parameter for.
+ */
+ virtual QString removeAllItemsString(unsigned count) const;
+
+ protected slots:
+ /**
+ * If using the popup menu handing, the subclass needs to reimplement this
+ * function to remove the selected item, which is passed in as a
+ * parameter.
+ */
+ virtual void removeSelectedItem(QListViewItem *item);
+
+ /**
+ * If using the popup menu handling, the subclass needs to reimplement this
+ * function to remove all items.
+ */
+ virtual void removeAllItems();
+
+ /**
+ * If using the popup menu handling, this function may be called to
+ * determine whether the selected item given by @p item is removable.
+ */
+ virtual bool isItemRemovable(QListViewItem *item) const;
+
+ private slots:
+ void rightClicked(QListViewItem *item, const QPoint &pt);
+ void removeSelected();
+
+ private:
+ KPopupMenu *m_menu;
+ bool m_usePopup;
+
+ int m_removeSingleId;
+ int m_removeAllId;
+};
+
+class ValueListViewItem : public KListViewItem
+{
+ public:
+ ValueListViewItem (QListView *listView, const QString &name, const Abakus::number_t &value);
+
+ // Will cause the list item to rethink the text.
+ void valueChanged();
+ void valueChanged(const Abakus::number_t &newValue);
+
+ Abakus::number_t itemValue() const;
+
+ private:
+ Abakus::number_t m_value;
+};
+
+/**
+ * Subclass used for the list of variables.
+ */
+class VariableListView : public ListView
+{
+ Q_OBJECT
+ public:
+
+ VariableListView(QWidget *parent, const char *name = 0);
+
+ protected:
+ virtual QString removeItemString() const;
+ virtual QString removeAllItemsString(unsigned count) const;
+ virtual bool isItemRemovable(QListViewItem *item) const;
+
+ protected slots:
+ virtual void removeSelectedItem(QListViewItem *item);
+ virtual void removeAllItems();
+};
+
+/**
+ * Subclass used for the list of functions.
+ */
+class FunctionListView : public ListView
+{
+ Q_OBJECT
+ public:
+
+ FunctionListView(QWidget *parent, const char *name = 0);
+
+ protected:
+ virtual QString removeItemString() const;
+ virtual QString removeAllItemsString(unsigned count) const;
+ virtual bool isItemRemovable(QListViewItem *item) const;
+
+ protected slots:
+ virtual void removeSelectedItem(QListViewItem *item);
+ virtual void removeAllItems();
+};
+
+#endif
diff --git a/src/abakusui.rc b/src/abakusui.rc
new file mode 100644
index 0000000..395323e
--- /dev/null
+++ b/src/abakusui.rc
@@ -0,0 +1,35 @@
+<!DOCTYPE kpartgui>
+<kpartgui name="abakus" version="2">
+<MenuBar>
+ <Menu name="view"><text>&amp;View</text>
+ <Action name="toggleHistoryList"/>
+ <Action name="toggleVariableList"/>
+ <Action name="toggleFunctionList"/>
+
+ <Separator/>
+
+ <Action name="precisionAuto"/>
+ <Action name="precision3"/>
+ <Action name="precision8"/>
+ <Action name="precision15"/>
+ <Action name="precision50"/>
+ <Action name="precisionCustom"/>
+
+ <Separator/>
+
+ <Action name="toggleCompactMode"/>
+
+ <Separator/>
+
+ <Action name="clearHistory"/>
+ </Menu>
+ <Menu name="mode"><text>&amp;Mode</text>
+ <Action name="setDegreesMode"/>
+ <Action name="setRadiansMode"/>
+
+ <Separator/>
+
+ <Action name="toggleExpressionMode"/>
+ </Menu>
+</MenuBar>
+</kpartgui>
diff --git a/src/dcopIface.h b/src/dcopIface.h
new file mode 100644
index 0000000..505f411
--- /dev/null
+++ b/src/dcopIface.h
@@ -0,0 +1,47 @@
+#ifndef ABAKUS_DCOP_IFACE
+#define ABAKUS_DCOP_IFACE
+/*
+ * dcopIface.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include <kdebug.h>
+#include <dcopobject.h>
+
+#include <qstring.h>
+
+#include "mainwindow.h"
+#include "numerictypes.h"
+#include "function.h"
+
+class AbakusIface : virtual public DCOPObject
+{
+ K_DCOP
+ public:
+ AbakusIface() : DCOPObject("Calculator")
+ {
+ }
+
+ k_dcop:
+ virtual double evaluate(const QString &expr)
+ {
+ Abakus::number_t result = parseString(expr.latin1());
+ return result.asDouble();
+ }
+};
+
+#endif
diff --git a/src/dragsupport.cpp b/src/dragsupport.cpp
new file mode 100644
index 0000000..0a8b875
--- /dev/null
+++ b/src/dragsupport.cpp
@@ -0,0 +1,87 @@
+/*
+ * dragsupport.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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include <qstring.h>
+#include <qpixmap.h>
+#include <qimage.h>
+#include <qpainter.h>
+#include <qcolor.h>
+#include <qfont.h>
+#include <qbrush.h>
+#include <qfontmetrics.h>
+
+#include "dragsupport.h"
+
+namespace DragSupport
+{
+
+QPixmap makePixmap(const QString &text, const QFont &font)
+{
+ QColor background(234, 178, 230);
+ QFontMetrics fm(font);
+
+ int height = 2 * fm.height();
+ QSize bonusSize (height, 0);
+ QSize size(fm.width(text), height);
+ QImage image(size + bonusSize, 32);
+
+ image.setAlphaBuffer(false);
+ image.fill(0); // All transparent pixels
+ image.setAlphaBuffer(true);
+
+ QPixmap pix(size + bonusSize);
+ pix.fill(Qt::magenta); // Watch for incoming hacks
+
+ QPainter painter(&pix);
+ painter.setFont(font);
+
+ // Outline black, background white
+ painter.setPen(Qt::black);
+ painter.setBrush(background);
+
+ // roundRect is annoying in that the four "pies" in each corner aren't
+ // circular, they're elliptical. Try to make the radii force it circular
+ // again.
+ painter.drawRoundRect(pix.rect(), 75 * pix.height() / pix.width(), 75);
+
+ // Alias better names for some constants.
+ int textLeft = height / 2;
+
+ // Draw text
+ painter.setPen(Qt::black);
+ painter.drawText(textLeft, height / 4, size.width(), size.height(), 0, text);
+
+ QImage overlay(pix.convertToImage());
+
+ // The images should have the same size, copy pixels from overlay to the
+ // bottom unless the pixel is called magenta. The pixels we don't copy
+ // are transparent in the QImage, and will remain transparent when
+ // converted to a QPixmap.
+
+ for(int i = 0; i < image.width(); ++i)
+ for(int j = 0; j < image.height(); ++j) {
+ if(QColor(overlay.pixel(i, j)) != Qt::magenta)
+ image.setPixel(i, j, overlay.pixel(i, j));
+ }
+
+ pix.convertFromImage(image);
+ return pix;
+}
+
+} // DragSupport
diff --git a/src/dragsupport.h b/src/dragsupport.h
new file mode 100644
index 0000000..76ebe81
--- /dev/null
+++ b/src/dragsupport.h
@@ -0,0 +1,33 @@
+#ifndef ABAKUS_DRAGSUPPORT_H
+#define ABAKUS_DRAGSUPPORT_H
+/*
+ * dragsupport.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+class QPixmap;
+class QString;
+class QFont;
+
+namespace DragSupport {
+
+ QPixmap makePixmap(const QString &text, const QFont &font);
+
+}
+#endif
+
+// vim: set et ts=8 sw=4:
diff --git a/src/editor.cpp b/src/editor.cpp
new file mode 100644
index 0000000..1ae425f
--- /dev/null
+++ b/src/editor.cpp
@@ -0,0 +1,892 @@
+/* This file was part of the SpeedCrunch project
+ Copyright (C) 2004,2005 Ariya Hidayat <ariya@kde.org>
+
+ And is now part of abakus.
+ Copyright (c) 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., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA.
+ */
+
+#include "function.h"
+#include "valuemanager.h"
+#include "editor.h"
+#include "evaluator.h"
+#include "result.h"
+
+#include <qapplication.h>
+#include <qlabel.h>
+#include <qlineedit.h>
+#include <qlistbox.h>
+#include <qpainter.h>
+#include <qregexp.h>
+#include <qstringlist.h>
+#include <qstyle.h>
+#include <qsyntaxhighlighter.h>
+#include <qtimer.h>
+#include <qtooltip.h>
+#include <qmessagebox.h>
+#include <qvbox.h>
+
+#include <netwm.h>
+#include <fixx11h.h> // netwm.h includes X11 headers which conflict with qevent
+#include <qevent.h>
+
+#include <kdebug.h>
+
+#include <algorithm>
+
+// XXX: QT 4: Replace this with qBinaryFind().
+using std::binary_search;
+
+class CalcResultLabel : public QLabel
+{
+public:
+ CalcResultLabel(QWidget *parent, const char *name, int WFlags) :
+ QLabel(parent, name, WFlags)
+ {
+ }
+
+protected:
+ virtual void mousePressEvent(QMouseEvent *)
+ {
+ hide();
+ }
+};
+
+class EditorHighlighter : public QSyntaxHighlighter
+{
+public:
+ EditorHighlighter( Editor* );
+ int highlightParagraph ( const QString & text, int );
+
+private:
+ Editor* editor;
+};
+
+class Editor::Private
+{
+public:
+ Evaluator* eval;
+ QStringList history;
+ int index;
+ bool autoCompleteEnabled;
+ EditorCompletion* completion;
+ QTimer* completionTimer;
+ bool autoCalcEnabled;
+ char format;
+ int decimalDigits;
+ QTimer* autoCalcTimer;
+ QLabel* autoCalcLabel;
+ bool syntaxHighlightEnabled;
+ EditorHighlighter* highlighter;
+ QMap<ColorType,QColor> highlightColors;
+ QTimer* matchingTimer;
+};
+
+class EditorCompletion::Private
+{
+public:
+ Editor* editor;
+ QVBox *completionPopup;
+ QListBox *completionListBox;
+};
+
+class ChoiceItem: public QListBoxText
+{
+ public:
+ ChoiceItem( QListBox*, const QString& );
+ void setMinNameWidth (int w) { minNameWidth = w; }
+ int nameWidth() const;
+
+ protected:
+ void paint( QPainter* p );
+
+ private:
+ QString item;
+ QString desc;
+ int minNameWidth;
+};
+
+ChoiceItem::ChoiceItem( QListBox* listBox, const QString& text ):
+ QListBoxText( listBox, text ), minNameWidth(0)
+{
+ QStringList list = QStringList::split( ':', text );
+ if( list.count() ) item = list[0];
+ if( list.count()>1 ) desc = list[1];
+}
+
+// Returns width of this particular list item's name.
+int ChoiceItem::nameWidth() const
+{
+ if(item.isEmpty())
+ return 0;
+
+ QFontMetrics fm = listBox()->fontMetrics();
+ return fm.width( item );
+}
+
+void ChoiceItem::paint( QPainter* painter )
+{
+ int itemHeight = height( listBox() );
+ QFontMetrics fm = painter->fontMetrics();
+ int yPos = ( ( itemHeight - fm.height() ) / 2 ) + fm.ascent();
+ painter->drawText( 3, yPos, item );
+
+ //int xPos = fm.width( item );
+ int xPos = QMAX(fm.width(item), minNameWidth);
+ if( !isSelected() )
+ painter->setPen( listBox()->palette().disabled().text().dark() );
+ painter->drawText( 10 + xPos, yPos, desc );
+}
+
+EditorHighlighter::EditorHighlighter( Editor* e ):
+ QSyntaxHighlighter( e )
+{
+ editor = e;
+}
+
+int EditorHighlighter::highlightParagraph ( const QString & text, int )
+{
+ if( !editor->isSyntaxHighlightEnabled() )
+ {
+ setFormat( 0, text.length(), editor->colorGroup().text() );
+ return 0;
+ }
+
+ QStringList fnames = FunctionManager::instance()->functionList(FunctionManager::All);
+ fnames.sort(); // Sort list so we can bin search it.
+
+ Tokens tokens = Evaluator::scan( text );
+ for( unsigned i = 0; i < tokens.count(); i++ )
+ {
+ Token& token = tokens[i];
+ QString text = token.text().lower();
+ QFont font = editor->font();
+ QColor color = Qt::black;
+ switch( token.type() )
+ {
+ case Token::Number:
+ color = editor->highlightColor( Editor::Number );
+ break;
+
+ case Token::Identifier:
+ {
+ color = editor->highlightColor( Editor::Variable );
+ if( binary_search( fnames.constBegin(), fnames.constEnd(), text) ) {
+ color = editor->highlightColor( Editor::FunctionName );
+ }
+ }
+ break;
+
+ case Token::Operator:
+ break;
+
+ default: break;
+ };
+ if( token.pos() >= 0 ) {
+ setFormat( token.pos(), token.text().length(), font, color );
+ }
+ }
+ return 0;
+}
+
+
+
+Editor::Editor( QWidget* parent, const char* name ):
+ QTextEdit( parent, name )
+{
+ d = new Private;
+ d->eval = 0;
+ d->index = 0;
+ d->autoCompleteEnabled = true;
+ d->completion = new EditorCompletion( this );
+ d->completionTimer = new QTimer( this );
+ d->autoCalcEnabled = true;
+ d->syntaxHighlightEnabled = true;
+ d->highlighter = new EditorHighlighter( this );
+ d->autoCalcTimer = new QTimer( this );
+ d->matchingTimer = new QTimer( this );
+
+ setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Fixed );
+ setWordWrap( NoWrap );
+ setHScrollBarMode( AlwaysOff );
+ setVScrollBarMode( AlwaysOff );
+ setTextFormat( PlainText );
+ setAutoFormatting( AutoNone );
+ setTabChangesFocus( true );
+ setLinkUnderline( false );
+
+ connect( d->completion, SIGNAL( selectedCompletion( const QString& ) ),
+ SLOT( autoComplete( const QString& ) ) );
+ connect( this, SIGNAL( textChanged() ), SLOT( checkAutoComplete() ) );
+ connect( d->completionTimer, SIGNAL( timeout() ), SLOT( triggerAutoComplete() ) );
+
+ connect( this, SIGNAL( textChanged() ), SLOT( checkMatching() ) );
+ connect( d->matchingTimer, SIGNAL( timeout() ), SLOT( doMatchingLeft() ) );
+ connect( d->matchingTimer, SIGNAL( timeout() ), SLOT( doMatchingRight() ) );
+ connect( this, SIGNAL( textChanged() ), SLOT( checkAutoCalc() ) );
+ connect( d->autoCalcTimer, SIGNAL( timeout() ), SLOT( autoCalc() ) );
+ d->autoCalcLabel = new CalcResultLabel( 0, "autocalc", WStyle_StaysOnTop |
+ WStyle_Customize | WStyle_NoBorder | WStyle_Tool | WX11BypassWM );
+ d->autoCalcLabel->setFrameStyle( QFrame::Plain | QFrame::Box );
+ d->autoCalcLabel->setPalette( QToolTip::palette() );
+ d->autoCalcLabel->hide();
+
+ setHighlightColor( Number, QColor(0,0,127) );
+ setHighlightColor( FunctionName, QColor(85,0,0) );
+ setHighlightColor( Variable, QColor(0,85,0) );
+ setHighlightColor( MatchedPar, QColor(255,255,183) );
+}
+
+Editor::~Editor()
+{
+ d->autoCalcLabel->hide();
+ delete d;
+}
+
+QSize Editor::sizeHint() const
+{
+ constPolish();
+ QFontMetrics fm = fontMetrics();
+ int h = QMAX(fm.lineSpacing(), 14);
+ int w = fm.width( 'x' ) * 20;
+ int m = frameWidth() * 2;
+ return( style().sizeFromContents(QStyle::CT_LineEdit, this,
+ QSize( w + m, h + m + 4 ).
+ expandedTo(QApplication::globalStrut())));
+}
+
+QStringList Editor::history() const
+{
+ return d->history;
+}
+
+void Editor::setHistory( const QStringList& h )
+{
+ d->history = h;
+ d->index = d->history.count();
+}
+
+bool Editor::autoCompleteEnabled() const
+{
+ return d->autoCompleteEnabled;
+}
+
+void Editor::setAutoCompleteEnabled( bool enable )
+{
+ d->autoCompleteEnabled = enable;
+}
+
+bool Editor::autoCalcEnabled() const
+{
+ return d->autoCalcEnabled;
+}
+
+void Editor::setAutoCalcEnabled( bool enable )
+{
+ d->autoCalcEnabled = enable;
+}
+
+void Editor::setFormat( char format )
+{
+ d->format = format;
+}
+
+void Editor::setDecimalDigits( int digits )
+{
+ d->decimalDigits = digits;
+}
+
+void Editor::appendHistory( const QString& text )
+{
+ if( text.isEmpty() ) return;
+
+ QString lastText;
+ if( d->history.count() )
+ lastText = d->history[ d->history.count()-1 ];
+ if( text == lastText ) return;
+
+ d->history.append( text );
+ d->index = d->history.count()-1;
+}
+
+void Editor::clearHistory()
+{
+ d->history.clear();
+ d->index = 0;
+}
+
+void Editor::squelchNextAutoCalc()
+{
+ d->autoCalcTimer->stop();
+}
+
+void Editor::setText(const QString &txt)
+{
+ QTextEdit::setText(txt);
+ squelchNextAutoCalc();
+}
+
+void Editor::checkAutoComplete()
+{
+ if( !d->autoCompleteEnabled ) return;
+
+ d->completionTimer->stop();
+ d->completionTimer->start( 500, true );
+}
+
+void Editor::checkMatching()
+{
+ if( !d->syntaxHighlightEnabled ) return;
+
+ d->matchingTimer->stop();
+ d->matchingTimer->start( 200, true );
+}
+
+void Editor::checkAutoCalc()
+{
+ // Calc-As-You-Type
+ if( !d->autoCalcEnabled ) return;
+
+ d->autoCalcTimer->stop();
+ d->autoCalcTimer->start( 1000, true );
+ d->autoCalcLabel->hide();
+}
+
+void Editor::doMatchingLeft()
+{
+ if( !d->syntaxHighlightEnabled ) return;
+
+ // tokenize the expression
+ int para = 0, curPos = 0;
+ getCursorPosition( &para, &curPos );
+
+ // check for right par
+ QString subtext = text().left( curPos );
+ Tokens tokens = Evaluator::scan( subtext );
+ if( !tokens.valid() ) return;
+ if( tokens.count()<1 ) return;
+ Token lastToken = tokens[ tokens.count()-1 ];
+
+ // right par ?
+ if( lastToken.isOperator() )
+ if( lastToken.asOperator() == Token::RightPar )
+ if( lastToken.pos() == curPos-1 )
+ {
+ // find the matching left par
+ unsigned par = 1;
+ int k = 0;
+ Token matchToken;
+ int matchPos = -1;
+
+ for( k = tokens.count()-2; k >= 0; k-- )
+ {
+ if( par < 1 ) break;
+ Token matchToken = tokens[k];
+ if( matchToken.isOperator() )
+ {
+ if( matchToken.asOperator() == Token::RightPar )
+ par++;
+ if( matchToken.asOperator() == Token::LeftPar )
+ par--;
+ if( par == 0 ) matchPos = matchToken.pos();
+ }
+ }
+
+ if( matchPos >= 0 )
+ {
+ setSelection( 0, matchPos, 0, matchPos+1, 2 );
+ setSelection( 0, lastToken.pos(), 0, lastToken.pos()+1, 1 );
+ setCursorPosition( para, curPos );
+ }
+ }
+}
+
+void Editor::doMatchingRight()
+{
+ if( !d->syntaxHighlightEnabled ) return;
+
+ // tokenize the expression
+ int para = 0, curPos = 0;
+ getCursorPosition( &para, &curPos );
+
+ // check for left par
+ QString subtext = text().right( text().length() - curPos );
+ Tokens tokens = Evaluator::scan( subtext );
+ if( !tokens.valid() ) return;
+ if( tokens.count()<1 ) return;
+ Token firstToken = tokens[ 0 ];
+
+ // left par ?
+ if( firstToken.isOperator() )
+ if( firstToken.asOperator() == Token::LeftPar )
+ if( firstToken.pos() == 0 )
+ {
+ // find the matching right par
+ unsigned par = 1;
+ unsigned int k = 0;
+ Token matchToken;
+ int matchPos = -1;
+
+ for( k = 1; k < tokens.count(); k++ )
+ {
+ if( par < 1 ) break;
+ Token matchToken = tokens[k];
+ if( matchToken.isOperator() )
+ {
+ if( matchToken.asOperator() == Token::LeftPar )
+ par++;
+ if( matchToken.asOperator() == Token::RightPar )
+ par--;
+ if( par == 0 ) matchPos = matchToken.pos();
+ }
+ }
+
+ if( matchPos >= 0 )
+ {
+ setSelection( 0, curPos+matchPos, 0, curPos+matchPos+1, 2 );
+ setSelection( 0, curPos+firstToken.pos(), 0, curPos+firstToken.pos()+1, 1 );
+ setCursorPosition( para, curPos );
+ }
+ }
+
+}
+
+void Editor::triggerAutoComplete()
+{
+ if( !d->autoCompleteEnabled ) return;
+
+ // tokenize the expression (don't worry, this is very fast)
+ // faster now that it uses flex. ;)
+ int para = 0, curPos = 0;
+ getCursorPosition( &para, &curPos );
+ QString subtext = text().left( curPos );
+ Tokens tokens = Evaluator::scan( subtext );
+ if(!tokens.valid())
+ {
+ kdWarning() << "invalid tokens.\n";
+ return;
+ }
+
+ if(tokens.isEmpty() || subtext.endsWith(" "))
+ return;
+
+ Token lastToken = tokens[ tokens.count()-1 ];
+
+ // last token must be an identifier
+ if( !lastToken.isIdentifier() )
+ return;
+
+ QString id = lastToken.text();
+ if( id.isEmpty() )
+ return;
+
+ // find matches in function names
+ QStringList fnames = FunctionManager::instance()->functionList(FunctionManager::All);
+ QStringList choices;
+
+ for( unsigned i=0; i<fnames.count(); i++ )
+ if( fnames[i].startsWith( id, false ) )
+ {
+ QString str = fnames[i];
+
+ ::Function* f = FunctionManager::instance()->function( str );
+ if( f && !f->description.isEmpty() )
+ str.append( ':' ).append( f->description );
+
+ choices.append( str );
+ }
+
+ choices.sort();
+
+ // find matches in variables names
+ QStringList vchoices;
+ QStringList values = ValueManager::instance()->valueNames();
+
+ for(QStringList::ConstIterator it = values.begin(); it != values.end(); ++it)
+ if( (*it).startsWith( id, false ) )
+ {
+ QString choice = ValueManager::description(*it);
+ if(choice.isEmpty())
+ choice = ValueManager::instance()->value(*it).toString();
+
+ vchoices.append( QString("%1:%2").arg( *it, choice ) );
+ }
+
+ vchoices.sort();
+ choices += vchoices;
+
+ // no match, don't bother with completion
+ if( !choices.count() ) return;
+
+ // one match, complete it for the user
+ if( choices.count()==1 )
+ {
+ QString str = QStringList::split( ':', choices[0] )[0];
+
+ // single perfect match, no need to give choices.
+ if(str == id.lower())
+ return;
+
+ str = str.remove( 0, id.length() );
+ int para = 0, curPos = 0;
+ getCursorPosition( &para, &curPos );
+ blockSignals( true );
+ insert( str );
+ setSelection( 0, curPos, 0, curPos+str.length() );
+ blockSignals( false );
+ return;
+ }
+
+ // present the user with completion choices
+ d->completion->showCompletion( choices );
+}
+
+void Editor::autoComplete( const QString& item )
+{
+ if( !d->autoCompleteEnabled || item.isEmpty() )
+ return;
+
+ int para = 0, curPos = 0;
+ getCursorPosition( &para, &curPos );
+
+ QString subtext = text().left( curPos );
+ Tokens tokens = Evaluator::scan( subtext );
+
+ if( !tokens.valid() || tokens.count() < 1 )
+ return;
+
+ Token lastToken = tokens[ tokens.count()-1 ];
+ if( !lastToken.isIdentifier() )
+ return;
+
+ QStringList str = QStringList::split( ':', item );
+
+ blockSignals( true );
+ setSelection( 0, lastToken.pos(), 0, lastToken.pos()+lastToken.text().length() );
+ insert( str[0] );
+ blockSignals( false );
+}
+
+void Editor::autoCalc()
+{
+ if( !d->autoCalcEnabled )
+ return;
+
+ QString str = Evaluator::autoFix( text() );
+ if( str.isEmpty() )
+ return;
+
+ // too short? do not bother...
+ Tokens tokens = Evaluator::scan( str );
+ if( tokens.count() < 2 )
+ return;
+
+ // If we're using set for a function don't try.
+ QRegExp setFn("\\s*set.*\\(.*=");
+ if( str.find(setFn) != -1 )
+ return;
+
+ // strip off assignment operator, e.g. "x=1+2" becomes "1+2" only
+ // the reason is that we want only to evaluate (on the fly) the expression,
+ // not to update (put the result in) the variable
+ if( tokens.count() > 2 && tokens[0].isIdentifier() &&
+ tokens[1].asOperator() == Token::Equal )
+ {
+ Tokens::const_iterator it = tokens.begin();
+ ++it;
+ ++it; // Skip first two tokens.
+
+ // Reconstruct string to evaluate using the tokens.
+ str = "";
+ while(it != tokens.end())
+ {
+ str += (*it).text();
+ str += ' ';
+ ++it;
+ }
+ }
+
+ Abakus::number_t result = parseString(str.latin1());
+ if( Result::lastResult()->type() == Result::Value )
+ {
+ QString ss = QString("Result: <b>%2</b>").arg(result.toString());
+ d->autoCalcLabel->setText( ss );
+ d->autoCalcLabel->adjustSize();
+
+ // reposition nicely
+ QPoint pos = mapToGlobal( QPoint( 0, 0 ) );
+ pos.setY( pos.y() - d->autoCalcLabel->height() - 1 );
+ d->autoCalcLabel->move( pos );
+ d->autoCalcLabel->show();
+ d->autoCalcLabel->raise();
+
+ // do not show it forever
+ QTimer::singleShot( 5000, d->autoCalcLabel, SLOT( hide()) );
+ }
+ else
+ {
+ // invalid expression
+ d->autoCalcLabel->hide();
+ }
+}
+
+QString Editor::formatNumber( const Abakus::number_t &value ) const
+{
+ return value.toString();
+}
+
+void Editor::historyBack()
+{
+ if( d->history.isEmpty() )
+ return;
+
+ d->index--;
+
+ if( d->index < 0 )
+ d->index = 0;
+
+ setText( d->history[ d->index ] );
+ setCursorPosition( 0, text().length() );
+ ensureCursorVisible();
+}
+
+void Editor::historyForward()
+{
+ if( d->history.isEmpty() )
+ return;
+
+ d->index++;
+
+ if( d->index >= (int) d->history.count() )
+ d->index = d->history.count() - 1;
+
+ setText( d->history[ d->index ] );
+ setCursorPosition( 0, text().length() );
+ ensureCursorVisible();
+}
+
+void Editor::keyPressEvent( QKeyEvent* e )
+{
+ if( e->key() == Key_Up )
+ {
+ historyBack();
+ e->accept();
+ return;
+ }
+
+ if( e->key() == Key_Down )
+ {
+ historyForward();
+ e->accept();
+ return;
+ }
+
+ if( e->key() == Key_Enter || e->key() == Key_Return )
+ {
+ emit returnPressed();
+ return;
+ }
+
+ if( e->key() == Key_Left ||
+ e->key() == Key_Right ||
+ e->key() == Key_Home ||
+ e->key() == Key_End )
+ {
+ checkMatching();
+ }
+
+ QTextEdit::keyPressEvent( e );
+}
+
+void Editor::wheelEvent( QWheelEvent *e )
+{
+ if( e->delta() > 0 )
+ historyBack();
+ else if( e->delta() < 0 )
+ historyForward();
+
+ e->accept();
+}
+
+void Editor::setSyntaxHighlight( bool enable )
+{
+ d->syntaxHighlightEnabled = enable;
+ d->highlighter->rehighlight();
+}
+
+bool Editor::isSyntaxHighlightEnabled() const
+{
+ return d->syntaxHighlightEnabled;
+}
+
+void Editor::setHighlightColor( ColorType type, QColor color )
+{
+ d->highlightColors[ type ] = color;
+
+ setSelectionAttributes( 1, highlightColor( Editor::MatchedPar ), false );
+ setSelectionAttributes( 2, highlightColor( Editor::MatchedPar ), false );
+
+ d->highlighter->rehighlight();
+}
+
+QColor Editor::highlightColor( ColorType type )
+{
+ return d->highlightColors[ type ];
+}
+
+
+EditorCompletion::EditorCompletion( Editor* editor ): QObject( editor )
+{
+ d = new Private;
+ d->editor = editor;
+
+ d->completionPopup = new QVBox( editor->topLevelWidget(), 0, WType_Popup );
+ d->completionPopup->setFrameStyle( QFrame::Box | QFrame::Plain );
+ d->completionPopup->setLineWidth( 1 );
+ d->completionPopup->installEventFilter( this );
+ d->completionPopup->setSizePolicy( QSizePolicy::Expanding, QSizePolicy::Minimum);
+
+ d->completionListBox = new QListBox( d->completionPopup );
+ d->completionPopup->setFocusProxy( d->completionListBox );
+ d->completionListBox->setFrameStyle( QFrame::NoFrame );
+ d->completionListBox->setVariableWidth( true );
+ d->completionListBox->installEventFilter( this );
+}
+
+EditorCompletion::~EditorCompletion()
+{
+ delete d;
+}
+
+bool EditorCompletion::eventFilter( QObject *obj, QEvent *ev )
+{
+ if ( obj == d->completionPopup || obj == d->completionListBox )
+ {
+
+ if ( ev->type() == QEvent::KeyPress )
+ {
+ QKeyEvent *ke = (QKeyEvent*)ev;
+ if ( ke->key() == Key_Enter || ke->key() == Key_Return )
+ {
+ doneCompletion();
+ return true;
+ }
+ else if ( ke->key() == Key_Left || ke->key() == Key_Right ||
+ ke->key() == Key_Up || ke->key() == Key_Down ||
+ ke->key() == Key_Home || ke->key() == Key_End ||
+ ke->key() == Key_Prior || ke->key() == Key_Next )
+ return false;
+
+ d->completionPopup->close();
+ d->editor->setFocus();
+ QApplication::sendEvent( d->editor, ev );
+ return true;
+ }
+
+ if ( ev->type() == QEvent::MouseButtonDblClick )
+ {
+ doneCompletion();
+ return true;
+ }
+
+ }
+
+ return false;
+}
+
+void EditorCompletion::doneCompletion()
+{
+ d->completionPopup->close();
+ d->editor->setFocus();
+ emit selectedCompletion( d->completionListBox->currentText() );
+}
+
+void EditorCompletion::showCompletion( const QStringList &choices )
+{
+ static bool shown = false;
+ if( !choices.count() ) return;
+
+ d->completionListBox->clear();
+ int maxWidth = 0;
+ for( unsigned i = 0; i < choices.count(); i++ ) {
+ ChoiceItem *item = new ChoiceItem( d->completionListBox, choices[i] );
+ int itemMaxWidth = item->nameWidth();
+
+ if(itemMaxWidth > maxWidth)
+ maxWidth = itemMaxWidth;
+ }
+
+ for(unsigned i = 0; i < d->completionListBox->count(); ++i) {
+ ChoiceItem *item = static_cast<ChoiceItem *>(d->completionListBox->item(i));
+ item->setMinNameWidth(maxWidth);
+ }
+
+ d->completionListBox->setCurrentItem( 0 );
+
+ // size of the pop-up
+ d->completionPopup->setMaximumHeight( 120 );
+ d->completionPopup->resize( d->completionListBox->sizeHint() +
+ QSize( d->completionListBox->verticalScrollBar()->width() + 4,
+ d->completionListBox->horizontalScrollBar()->height() + 4 ) );
+
+ if(!shown)
+ {
+ d->completionPopup->show();
+ QTimer::singleShot ( 0, this, SLOT(moveCompletionPopup()) );
+ }
+ else
+ {
+ moveCompletionPopup();
+ d->completionPopup->show();
+ }
+}
+
+void EditorCompletion::moveCompletionPopup()
+{
+ int h = d->completionListBox->height();
+ int w = d->completionListBox->width();
+
+ // position, reference is editor's cursor position in global coord
+ QFontMetrics fm( d->editor->font() );
+ int para = 0, curPos = 0;
+
+ d->editor->getCursorPosition( &para, &curPos );
+
+ int pixelsOffset = fm.width( d->editor->text(), curPos );
+ pixelsOffset -= d->editor->contentsX();
+ QPoint pos = d->editor->mapToGlobal( QPoint( pixelsOffset, d->editor->height() ) );
+
+ // if popup is partially invisible, move to other position
+ NETRootInfo info(d->completionPopup->x11Display(),
+ NET::CurrentDesktop | NET::WorkArea | NET::NumberOfDesktops,
+ -1, false);
+ info.activate(); // wtf is this needed for?
+ NETRect NETarea = info.workArea(info.currentDesktop());
+
+ QRect area(NETarea.pos.x, NETarea.pos.y, NETarea.size.width, NETarea.size.height);
+
+ if( pos.y() + h > area.y() + area.height() )
+ pos.setY( pos.y() - h - d->editor->height() );
+ if( pos.x() + w > area.x() + area.width() )
+ pos.setX( area.x() + area.width() - w );
+
+ d->completionPopup->move( pos );
+ d->completionListBox->setFocus();
+}
+
+#include "editor.moc"
+
+// vim: set et sw=2 ts=8:
diff --git a/src/editor.h b/src/editor.h
new file mode 100644
index 0000000..a71d661
--- /dev/null
+++ b/src/editor.h
@@ -0,0 +1,131 @@
+/* This file was part of the SpeedCrunch project
+ Copyright (C) 2004,2005 Ariya Hidayat <ariya@kde.org>
+
+ And is now part of abakus.
+ Copyright (c) 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., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA.
+ */
+
+
+#ifndef ABAKUS_EDITOR_H
+#define ABAKUS_EDITOR_H
+
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qtextedit.h>
+
+#include "hmath.h"
+
+class QEvent;
+class QKeyEvent;
+class QWidget;
+class Evaluator;
+
+class Editor : public QTextEdit
+{
+ Q_OBJECT
+
+ public:
+
+ typedef enum
+ {
+ Number, FunctionName, Variable, MatchedPar
+ } ColorType;
+
+ Editor( QWidget* parent = 0, const char* name = 0 );
+ ~Editor();
+
+ QSize sizeHint() const;
+ QSize xminimumSizeHint() const;
+
+ QStringList history() const;
+ void setHistory( const QStringList& history );
+
+ bool autoCompleteEnabled() const;
+ void setAutoCompleteEnabled( bool enable );
+
+ bool autoCalcEnabled() const;
+ void setAutoCalcEnabled( bool enable );
+ void setFormat( char format );
+ void setDecimalDigits( int digits );
+
+ void setSyntaxHighlight( bool enable );
+ bool isSyntaxHighlightEnabled() const;
+ void setHighlightColor( ColorType type, QColor color );
+ QColor highlightColor( ColorType type );
+
+ public slots:
+ void appendHistory( const QString& text );
+ void clearHistory();
+
+ // Stop the timer from going off.
+ void squelchNextAutoCalc();
+
+ void setText(const QString &txt);
+
+ protected slots:
+ void checkAutoComplete();
+ void triggerAutoComplete();
+ void autoComplete( const QString& item );
+ void checkAutoCalc();
+ void autoCalc();
+ void checkMatching();
+ void doMatchingLeft();
+ void doMatchingRight();
+ void historyBack();
+ void historyForward();
+
+ protected:
+ void keyPressEvent( QKeyEvent* );
+ void wheelEvent( QWheelEvent* );
+ QString formatNumber( const Abakus::number_t &value ) const;
+
+ private:
+ class Private;
+ Private* d;
+ Editor( const Editor& );
+ Editor& operator=( const Editor& );
+};
+
+
+class EditorCompletion : public QObject
+{
+ Q_OBJECT
+
+ public:
+ EditorCompletion( Editor* editor );
+ ~EditorCompletion();
+
+ bool eventFilter( QObject *o, QEvent *e );
+ void doneCompletion();
+ void showCompletion( const QStringList &choices );
+
+ protected slots:
+ void moveCompletionPopup();
+
+ signals:
+ void selectedCompletion( const QString& item );
+
+ private:
+ class Private;
+ Private* d;
+ EditorCompletion( const EditorCompletion& );
+ EditorCompletion& operator=( const EditorCompletion& );
+};
+
+#endif // ABAKUS_EDITOR_H
+
+// vim: set et ts=8 sw=4:
diff --git a/src/evaluator.cpp b/src/evaluator.cpp
new file mode 100644
index 0000000..4b47d5e
--- /dev/null
+++ b/src/evaluator.cpp
@@ -0,0 +1,261 @@
+/* This file was part of the SpeedCrunch project
+ Copyright (C) 2004 Ariya Hidayat <ariya@kde.org>
+
+ And is now part of abakus.
+ Copyright (c) 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., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA.
+ */
+
+#include "evaluator.h"
+#include "function.h"
+#include "node.h" // For parser_yacc.hpp below
+#include "parser_yacc.hpp"
+
+#include <qapplication.h>
+#include <qmap.h>
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qvaluevector.h>
+
+#include <kdebug.h>
+
+//
+// Reimplementation of goodies from Evaluator follows.
+//
+
+Evaluator::Evaluator()
+{
+}
+
+Evaluator::~Evaluator()
+{
+}
+
+void Evaluator::setExpression(const QString &expr)
+{
+ kdError() << k_funcinfo << " not implemented.\n";
+}
+
+QString Evaluator::expression() const
+{
+ kdError() << k_funcinfo << " not implemented.\n";
+ return QString();
+}
+
+void Evaluator::clear()
+{
+ kdError() << k_funcinfo << " not implemented.\n";
+ // Yeah, whatever.
+}
+
+bool Evaluator::isValid() const
+{
+ return true;
+}
+
+Tokens Evaluator::tokens() const
+{
+ kdError() << k_funcinfo << " not implemented.\n";
+ return Tokens();
+}
+
+Tokens Evaluator::scan(const QString &expr)
+{
+ Lexer l(expr);
+ Tokens tokens;
+
+ while(l.hasNext())
+ {
+ int t = l.nextType();
+ Token::Type type = Token::Unknown;
+
+ switch(t)
+ {
+ case POWER:
+ case '*':
+ case '(':
+ case ')':
+ case '-':
+ case '+':
+ case ',':
+ case '=':
+ type = Token::Operator;
+ break;
+
+ case NUM:
+ type = Token::Number;
+ break;
+
+ case SET:
+ case REMOVE:
+ case DERIV:
+ case FN:
+ case ID:
+ type = Token::Identifier;
+ break;
+
+ default:
+ type = Token::Unknown;
+ break;
+ }
+
+ tokens.append(Token(type, l.tokenValue(), l.tokenPos()));
+ }
+
+ return tokens;
+}
+
+QString Evaluator::error() const
+{
+ kdError() << k_funcinfo << " not implemented.\n";
+ return "No Error Yet";
+}
+
+///
+/// ARIYA'S CLASS CODE FOLLOWS
+///
+
+// for null token
+const Token Token::null;
+
+// helper function: return operator of given token text
+// e.g. "*" yields Operator::Asterisk, and so on
+static Token::Op matchOperator( const QString& text )
+{
+ Token::Op result = Token::InvalidOp;
+
+ if( text.length() == 1 )
+ {
+ QChar p = text[0];
+ switch( p.unicode() )
+ {
+ case '+': result = Token::Plus; break;
+ case '-': result = Token::Minus; break;
+ case '*': result = Token::Asterisk; break;
+ case '/': result = Token::Slash; break;
+ case '^': result = Token::Caret; break;
+ case ',': result = Token::Comma; break;
+ case '(': result = Token::LeftPar; break;
+ case ')': result = Token::RightPar; break;
+ case '%': result = Token::Percent; break;
+ case '=': result = Token::Equal; break;
+ default : result = Token::InvalidOp; break;
+ }
+ }
+
+ if( text.length() == 2 )
+ {
+ if( text == "**" ) result = Token::Caret;
+ }
+
+ return result;
+}
+
+// creates a token
+Token::Token( Type type, const QString& text, int pos )
+{
+ m_type = type;
+ m_text = text;
+ m_pos = pos;
+}
+
+// copy constructor
+Token::Token( const Token& token )
+{
+ m_type = token.m_type;
+ m_text = token.m_text;
+ m_pos = token.m_pos;
+}
+
+// assignment operator
+Token& Token::operator=( const Token& token )
+{
+ m_type = token.m_type;
+ m_text = token.m_text;
+ m_pos = token.m_pos;
+ return *this;
+}
+
+Abakus::number_t Token::asNumber() const
+{
+ if( isNumber() )
+ return Abakus::number_t( m_text.latin1() );
+ else
+ return Abakus::number_t();
+}
+
+Token::Op Token::asOperator() const
+{
+ if( isOperator() ) return matchOperator( m_text );
+ else return InvalidOp;
+}
+
+QString Token::description() const
+{
+ QString desc;
+
+ switch (m_type )
+ {
+ case Number: desc = "Number"; break;
+ case Identifier: desc = "Identifier"; break;
+ case Operator: desc = "Operator"; break;
+ default: desc = "Unknown"; break;
+ }
+
+ while( desc.length() < 10 ) desc.prepend( ' ' );
+ desc.prepend( " " );
+ desc.prepend( QString::number( m_pos ) );
+ desc.append( " : " ).append( m_text );
+
+ return desc;
+}
+
+
+QString Evaluator::autoFix( const QString& expr )
+{
+ int par = 0;
+ QString result;
+
+ // strip off all funny characters
+ for( unsigned c = 0; c < expr.length(); c++ )
+ if( expr[c] >= QChar(32) )
+ result.append( expr[c] );
+
+ // automagically close all parenthesis
+ Tokens tokens = Evaluator::scan( result );
+ for( unsigned i=0; i<tokens.count(); i++ )
+ if( tokens[i].asOperator() == Token::LeftPar ) par++;
+ else if( tokens[i].asOperator() == Token::RightPar ) par--;
+ for(; par > 0; par-- )
+ result.append( ')' );
+
+ // special treatment for simple function
+ // e.g. "cos" is regarded as "cos(ans)"
+ if( !result.isEmpty() )
+ {
+ Tokens tokens = Evaluator::scan( result );
+ if( (tokens.count() == 1) &&
+ FunctionManager::instance()->isFunction(tokens[0].text())
+ )
+ {
+ result.append( "(ans)" );
+ }
+ }
+
+ return result;
+}
+
+// vim: set et ts=8 sw=4:
diff --git a/src/evaluator.h b/src/evaluator.h
new file mode 100644
index 0000000..2d156eb
--- /dev/null
+++ b/src/evaluator.h
@@ -0,0 +1,131 @@
+/* This file was part of the SpeedCrunch project
+ Copyright (C) 2004 Ariya Hidayat <ariya@kde.org>
+
+ And is now part of abakus.
+ Copyright (c) 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., 59 Temple Place - Suite 330, Boston, MA 02110-1301, USA.
+ */
+
+#ifndef ABAKUS_EVALUATOR_H
+#define ABAKUS_EVALUATOR_H
+
+#include <qstring.h>
+#include <qvaluevector.h>
+
+#include "numerictypes.h"
+
+class Token
+{
+public:
+ typedef enum
+ {
+ Unknown,
+ Number,
+ Operator,
+ Identifier
+ } Type;
+
+ typedef enum
+ {
+ InvalidOp = 0,
+ Plus, // + (addition)
+ Minus, // - (substraction, negation)
+ Asterisk, // * (multiplication)
+ Slash, // / (division)
+ Caret, // ^ (power) or **.
+ LeftPar, // (
+ RightPar, // )
+ Comma, // argument separator
+ Percent,
+ Equal // variable assignment
+ } Op;
+
+ Token( Type type = Unknown, const QString& text = QString::null, int pos = -1 );
+
+ Token( const Token& );
+ Token& operator=( const Token& );
+
+ Type type() const { return m_type; }
+ QString text() const { return m_text; }
+ int pos() const { return m_pos; };
+
+ bool isNumber() const { return m_type == Number; }
+ bool isOperator() const { return m_type == Operator; }
+ bool isIdentifier() const { return m_type == Identifier; }
+
+ Abakus::number_t asNumber() const;
+ Op asOperator() const;
+
+ QString description() const;
+
+ static const Token null;
+
+protected:
+ Type m_type;
+ QString m_text;
+ int m_pos;
+};
+
+
+class Tokens: public QValueVector<Token>
+{
+public:
+ Tokens(): QValueVector<Token>(), m_valid(true) {};
+
+ bool valid() const { return m_valid; }
+ void setValid( bool v ) { m_valid = v; }
+
+protected:
+ bool m_valid;
+};
+
+class Variable
+{
+public:
+ QString name;
+ Abakus::number_t value;
+};
+
+class Evaluator
+{
+public:
+ Evaluator();
+ ~Evaluator();
+
+ void setExpression( const QString& expr );
+ QString expression() const;
+
+ void clear();
+ bool isValid() const;
+
+ Tokens tokens() const;
+ static Tokens scan( const QString& expr );
+
+ QString error() const;
+
+ // Abakus::number_t eval();
+
+ static QString autoFix( const QString& expr );
+
+private:
+ Evaluator( const Evaluator& );
+ Evaluator& operator=( const Evaluator& );
+};
+
+
+#endif // EVALUATOR
+
+// vim: set et ts=8 sw=4:
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 <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., 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"
diff --git a/src/function.h b/src/function.h
new file mode 100644
index 0000000..3b56cbb
--- /dev/null
+++ b/src/function.h
@@ -0,0 +1,122 @@
+#ifndef ABAKUS_FUNCTION_H
+#define ABAKUS_FUNCTION_H
+/*
+ * function.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include "numerictypes.h"
+
+#include <qobject.h>
+#include <qstringlist.h>
+#include <qstring.h>
+#include <qmap.h>
+#include <qdict.h>
+
+
+
+class BaseFunction;
+
+struct UserFunction
+{
+ int sequenceNumber;
+ BaseFunction *fn;
+ QString varName;
+};
+
+// Ugly pointer-to-member typedef ahead
+typedef Abakus::number_t (Abakus::number_t::*function_t)() const;
+
+struct Function {
+ QString name;
+ QString description;
+
+ // A function is either builtin or user defined, this union is
+ // used for both cases.
+ union {
+ function_t fn; // Builtin.
+ UserFunction *userFn; // User defined
+ };
+
+ bool returnsTrig;
+ bool needsTrig;
+
+ bool userDefined;
+};
+
+void setTrigMode(Abakus::TrigMode mode);
+Abakus::TrigMode trigMode();
+
+class FunctionManager : public QObject
+{
+ Q_OBJECT
+ public:
+ typedef QDict<Function> functionDict;
+
+ static FunctionManager *instance();
+
+ Function *function(const QString &name);
+
+ bool isFunction(const QString &name);
+ bool isFunctionUserDefined(const QString &name);
+
+ bool addFunction(BaseFunction *fn, const QString &dependantVar);
+ bool addFunction(const QString &name, function_t fn, const QString &desc);
+ void removeFunction(const QString &name);
+
+ typedef enum { Builtin, UserDefined, All } FunctionType;
+
+ QStringList functionList(FunctionType type);
+
+ signals:
+ void signalFunctionAdded(const QString &name);
+ void signalFunctionRemoved(const QString &name);
+
+ private:
+ FunctionManager(QObject *parent = 0, const char *name = "function manager");
+
+ static FunctionManager *m_manager;
+ functionDict m_dict;
+};
+
+Abakus::number_t evaluateFunction(const Function *func, const Abakus::number_t value);
+
+// Implemented in lexer.l due to prototype issues.
+Abakus::number_t parseString(const char *str);
+
+// Implemented in lexer.l due to prototype issues.
+class Lexer
+{
+public:
+ Lexer(const QString &expr);
+ ~Lexer();
+
+ bool hasNext() const;
+ int nextType();
+
+ int tokenPos() const;
+
+ // Can call this after nextType to find the associated string value of the
+ // token.
+ QString tokenValue() const;
+
+private:
+ class Private;
+ Private *m_private;
+};
+
+#endif
diff --git a/src/hi64-app-abakus.png b/src/hi64-app-abakus.png
new file mode 100644
index 0000000..5644c45
--- /dev/null
+++ b/src/hi64-app-abakus.png
Binary files differ
diff --git a/src/hmath.cpp b/src/hmath.cpp
new file mode 100644
index 0000000..8dcf346
--- /dev/null
+++ b/src/hmath.cpp
@@ -0,0 +1,1797 @@
+/* HMath: C++ high precision math routines
+ Copyright (C) 2004 Ariya Hidayat <ariya.hidayat@gmail.com>
+ Last update: November 15, 2004
+
+ This file was copied from the SpeedCrunch program. Please visit
+ http://speedcrunch.berlios.de/ for more information.
+
+ 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 "hmath.h"
+#include "number.h"
+
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include <qstring.h>
+
+// internal number of decimal digits
+#define HMATH_MAX_PREC 150
+
+// digits used for number comparison
+// (not all are used, to work around propagated error problem)
+#define HMATH_COMPARE_PREC 70
+
+// maximum shown digits if prec is negative
+#define HMATH_MAX_SHOWN 20
+
+// from number.c, need to be freed somehow
+extern bc_num _zero_;
+extern bc_num _one_;
+extern bc_num _two_;
+
+class HNumber::Private
+{
+public:
+ bc_num num;
+ bool nan;
+};
+
+void out_of_memory(void){
+ return;
+}
+
+void rt_warn(char * ,...){
+ return;
+}
+
+void rt_error(char * ,...){
+ return;
+}
+
+static bc_num h_create( int len = 1, int scale = 0 )
+{
+ bc_num temp;
+ temp = (bc_num) malloc( sizeof(bc_struct) );
+ temp->n_sign = PLUS;
+ temp->n_len = len;
+ temp->n_scale = scale;
+ temp->n_refs = 1;
+ temp->n_ptr = (char*) malloc( len+scale+1 );
+ temp->n_value = temp->n_ptr;
+ temp->n_next = 0;
+ memset (temp->n_ptr, 0, len+scale+1);
+ return temp;
+}
+
+static void h_destroy( bc_num n )
+{
+ free( n->n_ptr );
+ free( n );
+}
+
+// reclaim and free one bc_num from the freelist
+// workaround for number.c, because it doesn't really free a number
+// but instead put it in the pool of unused numbers
+// this function will take it back from that pool and set it really free
+static void h_grabfree()
+{
+ bc_num t = bc_new_num( 1, 0 );
+ h_destroy( t );
+}
+
+// make an exact (explicit) copy
+static bc_num h_copy( bc_num n )
+{
+ int len = n->n_len;
+ int scale = n->n_scale;
+ bc_num result = h_create( len, scale );
+ memcpy( result->n_ptr, n->n_value, len+scale+1 );
+ result->n_sign = n->n_sign;
+ result->n_value = result->n_ptr;
+ h_grabfree();
+ return result;
+}
+
+// same as copy, but readjust decimal digits
+static bc_num h_rescale( bc_num n, int sc )
+{
+ int len = n->n_len;
+ int scale = MIN( sc, n->n_scale );
+ bc_num result = h_create( len, scale );
+ memcpy( result->n_ptr, n->n_value, len+scale+1 );
+ result->n_sign = n->n_sign;
+ result->n_value = result->n_ptr;
+ h_grabfree();
+ return result;
+}
+
+// convert simple string to number
+static bc_num h_str2num( const char* str, int scale = HMATH_MAX_PREC )
+{
+ int digits, strscale;
+ const char *ptr;
+ char *nptr;
+ char zero_int;
+
+ /* Check for valid number and count digits. */
+ ptr = str;
+ digits = 0;
+ strscale = 0;
+ zero_int = FALSE;
+ if ( (*ptr == '+') || (*ptr == '-')) ptr++; /* Sign */
+ while (*ptr == '0') ptr++; /* Skip leading zeros. */
+ while (isdigit((int)*ptr)) ptr++, digits++; /* digits */
+ if (*ptr == '.') ptr++; /* decimal point */
+ while (isdigit((int)*ptr)) ptr++, strscale++; /* digits */
+ if ((*ptr != '\0') || (digits+strscale == 0))
+ return h_create();
+
+ /* Adjust numbers and allocate storage and initialize fields. */
+ strscale = MIN(strscale, scale);
+ if (digits == 0)
+ {
+ zero_int = TRUE;
+ digits = 1;
+ }
+
+ bc_num num = h_create( digits, strscale );
+
+ ptr = str;
+ if (*ptr == '-')
+ {
+ num->n_sign = MINUS;
+ ptr++;
+ }
+ else
+ {
+ num->n_sign = PLUS;
+ if (*ptr == '+') ptr++;
+ }
+ while (*ptr == '0') ptr++;
+ nptr = num->n_value;
+ if (zero_int)
+ {
+ *nptr++ = 0;
+ digits = 0;
+ }
+ for (;digits > 0; digits--)
+ *nptr++ = (char)CH_VAL(*ptr++);
+
+
+ if (strscale > 0)
+ {
+ ptr++;
+ for (;strscale > 0; strscale--)
+ *nptr++ = (char)CH_VAL(*ptr++);
+ }
+
+ return num;
+}
+
+
+// add two numbers, return newly allocated number
+static bc_num h_add( bc_num n1, bc_num n2 )
+{
+ bc_num r = h_create();
+ bc_add( n1, n2, &r, 1 );
+ h_grabfree();
+ return r;
+}
+
+// subtract two numbers, return newly allocated number
+static bc_num h_sub( bc_num n1, bc_num n2 )
+{
+ bc_num r = h_create();
+ bc_sub( n1, n2, &r, 1 );
+ h_grabfree();
+ return r;
+}
+
+// multiply two numbers, return newly allocated number
+static bc_num h_mul( bc_num n1, bc_num n2 )
+{
+ bc_num r = h_create();
+ bc_multiply( n1, n2, &r, HMATH_MAX_PREC );
+ h_grabfree();
+ return r;
+}
+
+// divide two numbers, return newly allocated number
+static bc_num h_div( bc_num n1, bc_num n2 )
+{
+ bc_num r = h_create();
+ bc_divide( n1, n2, &r, HMATH_MAX_PREC );
+ h_grabfree();
+ return r;
+}
+
+// find 10 raise to num
+// e.g.: when num is 5, it results 100000
+static bc_num h_raise10( int n )
+{
+ // calculate proper factor
+ int len = abs(n)+2;
+ char* sf = new char[len+1];
+ sf[len] = '\0';
+ if( n >= 0 )
+ {
+ sf[0] = '1';
+ sf[len-1] = '\0';
+ sf[len-2] = '\0';
+ for( int i = 0; i < n; i++ )
+ sf[i+1] = '0';
+ }
+ else
+ {
+ sf[0] = '0'; sf[1] = '.';
+ for( int i = 0; i < -n; i++ )
+ sf[i+2] = '0';
+ sf[len-1] = '1';
+ }
+
+ bc_num factor = h_str2num( sf, abs(n) );
+ delete[] sf;
+
+ return factor;
+}
+
+// round up to certain decimal digits
+static bc_num h_round( bc_num n, int prec )
+{
+ // no need to round?
+ if( prec >= n->n_scale )
+ return h_copy( n );
+
+ // example: rounding "3.14159" to 4 decimal digits means
+ // adding 0.5e-4 to 3.14159, it becomes 3.14164
+ // taking only 4 decimal digits, so it's now 3.1416
+ if( prec < 0 ) prec = 0;
+ bc_num x = h_raise10( -prec-1 );
+ bc_num y = 0;
+ bc_int2num( &y, 5 );
+ bc_num z = h_mul( x, y );
+ z->n_sign = n->n_sign;
+ bc_num r = h_add( n, z );
+ h_destroy( x );
+ h_destroy( y );
+ h_destroy( z );
+
+ // only digits we are interested in
+ bc_num v = h_rescale( r, prec );
+ h_destroy( r );
+
+ return v;
+}
+
+// remove trailing zeros
+static void h_trimzeros( bc_num num )
+{
+ while( ( num->n_scale > 0 ) && ( num->n_len+num->n_scale > 0 ) )
+ if( num->n_value[num->n_len+num->n_scale-1] == 0 )
+ num->n_scale--;
+ else break;
+}
+
+static void h_init()
+{
+ static bool h_initialized = false;
+ if( !h_initialized )
+ {
+ h_initialized = true;
+ bc_init_numbers();
+ }
+}
+
+
+HNumber::HNumber()
+{
+ h_init();
+ d = new Private;
+ d->nan = false;
+ d->num = h_create();
+}
+
+HNumber::HNumber( const HNumber& hn )
+{
+ h_init();
+ d = new Private;
+ d->nan = false;
+ d->num = h_create();
+ operator=( hn );
+}
+
+HNumber::HNumber( int i )
+{
+ h_init();
+ d = new Private;
+ d->nan = false;
+ d->num = h_create();
+ bc_int2num( &d->num, i );
+}
+
+HNumber::HNumber( const char* str )
+{
+ h_init();
+ d = new Private;
+ d->nan = false;
+ d->num = h_create();
+
+ if( str )
+ if( strlen(str) == 3 )
+ if( tolower(str[0])=='n' )
+ if( tolower(str[1])=='a' )
+ if( tolower(str[2])=='n' )
+ d->nan = true;
+
+ if( str && !d->nan )
+ {
+ char* s = new char[ strlen(str)+1 ];
+ strcpy( s, str );
+
+ char* p = s;
+ for( ;; p++ )
+ {
+ if( *p != '+' )
+ if( *p != '-' )
+ if( *p != '.' )
+ if( !isdigit(*p) )
+ break;
+ }
+
+ int expd = 0;
+
+ if( ( *p == 'e' ) || ( *p == 'E' ) )
+ {
+ *p = '\0';
+ expd = atoi( p+1 );
+ }
+
+ h_destroy( d->num );
+ d->num = h_str2num( s );
+ delete [] s;
+
+ if( expd >= HMATH_MAX_PREC || // too large
+ expd <= -HMATH_MAX_PREC ) // too small
+ {
+ d->nan = true;
+ }
+
+ if( expd != 0 )
+ {
+ bc_num factor = h_raise10( expd );
+ bc_num nn = h_copy( d->num );
+ h_destroy( d->num );
+ d->num = h_mul( nn, factor );
+ h_destroy( nn );
+ h_destroy( factor );
+ }
+ h_trimzeros( d->num );
+ }
+
+}
+
+HNumber::~HNumber()
+{
+ h_destroy( d->num );
+ delete d;
+}
+
+bool HNumber::isNan() const
+{
+ return d->nan;
+}
+
+bool HNumber::isZero() const
+{
+ return !d->nan && ( bc_is_zero( d->num )!=0 );
+}
+
+bool HNumber::isPositive() const
+{
+ return !d->nan && !isNegative() && !isZero();
+}
+
+bool HNumber::isNegative() const
+{
+ return !d->nan && ( bc_is_neg( d->num )!=0 );
+}
+
+HNumber HNumber::nan()
+{
+ HNumber n;
+ n.d->nan = true;
+ return n;
+}
+
+HNumber& HNumber::operator=( const HNumber& hn )
+{
+ d->nan = hn.d->nan;
+ h_destroy( d->num );
+ d->num = h_copy( hn.d->num );
+ return *this;
+}
+
+HNumber HNumber::operator+( const HNumber& num ) const
+{
+ if( isNan() ) return HNumber( *this );
+ if( num.isNan() ) return HNumber( num );
+
+ HNumber result;
+ h_destroy( result.d->num );
+ result.d->num = h_add( d->num, num.d->num );
+
+ return result;
+}
+
+HNumber& HNumber::operator+=( const HNumber& num )
+{
+ HNumber n = HNumber(*this) + num;
+ operator=( n );
+ return *this;
+}
+
+HNumber HNumber::operator-( const HNumber& num ) const
+{
+ if( isNan() ) return HNumber( *this );
+ if( num.isNan() ) return HNumber( num );
+
+ HNumber result;
+ h_destroy( result.d->num );
+ result.d->num = h_sub( d->num, num.d->num );
+
+ return result;
+}
+
+HNumber& HNumber::operator-=( const HNumber& num )
+{
+ HNumber n = HNumber(*this) - num;
+ operator=( n );
+ return *this;
+}
+
+HNumber HNumber::operator*( const HNumber& num ) const
+{
+ if( isNan() ) return HNumber( *this );
+ if( num.isNan() ) return HNumber( num );
+
+ HNumber result;
+ h_destroy( result.d->num );
+ result.d->num = h_mul( d->num, num.d->num );
+ return result;
+}
+
+HNumber& HNumber::operator*=( const HNumber& num )
+{
+ HNumber n = HNumber(*this) * num;
+ operator=( n );
+ return *this;
+}
+
+HNumber HNumber::operator/( const HNumber& num ) const
+{
+ if( isNan() ) return HNumber( *this );
+ if( num.isNan() ) return HNumber( num );
+
+ HNumber result;
+ h_destroy( result.d->num );
+ result.d->num = h_div( d->num, num.d->num );
+ if(num == HNumber(0))
+ result.d->nan = true;
+
+ return result;
+}
+
+HNumber& HNumber::operator/=( const HNumber& num )
+{
+ HNumber n = HNumber(*this) / num;
+ operator=( n );
+ return *this;
+}
+
+bool HNumber::operator>( const HNumber& n ) const
+{
+ return HMath::compare( *this, n ) > 0;
+}
+
+bool HNumber::operator<( const HNumber& n ) const
+{
+ return HMath::compare( *this, n ) < 0;
+}
+
+bool HNumber::operator>=( const HNumber& n ) const
+{
+ return HMath::compare( *this, n ) >= 0;
+}
+
+bool HNumber::operator<=( const HNumber& n ) const
+{
+ return HMath::compare( *this, n ) <= 0;
+}
+
+bool HNumber::operator==( const HNumber& n ) const
+{
+ return HMath::compare( *this, n ) == 0;
+}
+
+bool HNumber::operator!=( const HNumber& n ) const
+{
+ return HMath::compare( *this, n ) != 0;
+}
+
+
+// format number with fixed number of decimal digits
+char* HMath::formatFixed( const HNumber& hn, int prec )
+{
+ if( hn.isNan() )
+ {
+ char* str = (char*)malloc( 4 );
+ str[0] = 'N';
+ str[1] = 'a';
+ str[2] = 'N';
+ str[3] = '\0';
+ return str;
+ }
+
+ bc_num n = h_copy( hn.d->num );
+ h_trimzeros( n );
+
+ int oprec = prec;
+ if( prec < 0 )
+ {
+ prec = HMATH_MAX_SHOWN;
+ if( n->n_scale < HMATH_MAX_SHOWN )
+ prec = n->n_scale;
+ }
+
+ // yes, this is necessary!
+ bc_num m = h_round( n, prec );
+ h_trimzeros( m );
+ h_destroy( n );
+ n = m;
+ if( oprec < 0 )
+ {
+ prec = HMATH_MAX_SHOWN;
+ if( n->n_scale < HMATH_MAX_SHOWN )
+ prec = n->n_scale;
+ }
+
+ // how many to allocate?
+ int len = n->n_len + prec;
+ if( n->n_sign != PLUS ) len++;
+ if( prec > 0 ) len++;
+
+ char* str = (char*)malloc( len+1 );
+ char* p = str;
+
+ // the sign and the integer part
+ // but avoid printing "-0"
+ if( n->n_sign != PLUS )
+ if( !bc_is_zero( n ) ) *p++ = '-';
+ for( int c=0; c<n->n_len; c++ )
+ *p++ = (char)BCD_CHAR( n->n_value[c] );
+
+ // the fraction part
+ if( prec > 0 )
+ {
+ *p++ = '.';
+ int k = (prec < n->n_scale) ? prec : n->n_scale;
+ for( int d=0; d<k; d++ )
+ *p++ = (char)BCD_CHAR( n->n_value[n->n_len+d] );
+ for( int r=n->n_scale; r<prec; r++ )
+ *p++ = '0';
+ }
+
+ *p = '\0';
+ h_destroy( n );
+
+ return str;
+}
+
+// format number with exponential
+char* HMath::formatExp( const HNumber& hn, int prec )
+{
+ if( hn.isNan() )
+ {
+ char* str = (char*)malloc( 4 );
+ str[0] = 'N';
+ str[1] = 'a';
+ str[2] = 'N';
+ str[3] = '\0';
+ return str;
+ }
+
+ // find the exponent and the factor
+ int tzeros = 0;
+ for( int c=0; c<hn.d->num->n_len+hn.d->num->n_scale; c++, tzeros++ )
+ if( hn.d->num->n_value[c]!= 0 ) break;
+ int expd = hn.d->num->n_len - tzeros - 1;
+
+ // extra digits needed for the exponent part
+ int expn = 0;
+ for( int e = ::abs(expd); e > 0; e/=10 ) expn++;
+ if( expd <= 0 ) expn++;
+
+ // scale the number by a new factor
+ HNumber nn = hn * HMath::raise( 10, -expd );
+
+ // too close to zero?
+ if( hn.isZero() || ( expd <= -HMATH_COMPARE_PREC ) )
+ {
+ nn = HNumber(0);
+ expd = 0;
+ expn = 1;
+ }
+
+ char* str = formatFixed( nn, prec );
+ char* result = (char*) malloc( strlen(str)+expn+2 );
+ strcpy( result, str );
+ free( str );
+
+ // the exponential part
+ char* p = result + strlen(result);
+ *p++ = 'e'; p[expn] = '\0';
+ if( expd < 0 ) *p = '-';
+ for( int k=expn; k>0; k-- )
+ {
+ int digit = expd % 10;
+ p[k-1] = (char)('0' + ::abs( digit ));
+ expd = expd / 10;
+ if( expd == 0 ) break;
+ }
+
+ return result;
+}
+
+char* HMath::formatGeneral( const HNumber& hn, int prec )
+{
+ if( hn.isNan() )
+ {
+ char* str = (char*)malloc( 4 );
+ str[0] = 'N';
+ str[1] = 'a';
+ str[2] = 'N';
+ str[3] = '\0';
+ return str;
+ }
+
+ // find the exponent and the factor
+ int tzeros = 0;
+ for( int c=0; c<hn.d->num->n_len+hn.d->num->n_scale; c++, tzeros++ )
+ if( hn.d->num->n_value[c]!= 0 ) break;
+ int expd = hn.d->num->n_len - tzeros - 1;
+
+ char* str;
+ if( expd > 5 )
+ str = formatExp( hn, prec );
+ else if( ( expd < -4 ) && (expd>-HMATH_COMPARE_PREC ) )
+ str = formatExp( hn, prec );
+ else if ( (expd < 0) && (prec>0) && (expd < -prec) )
+ str = formatExp( hn, prec );
+ else
+ str = formatFixed( hn, prec );
+
+ return str;
+}
+
+QString HMath::formatGenString( const HNumber &n, int prec )
+{
+ char *foo = formatGeneral(n, prec);
+ QString s(foo);
+ free(foo);
+
+ return s;
+}
+
+char* HMath::format( const HNumber& hn, char format, int prec )
+{
+ if( hn.isNan() )
+ {
+ char* str = (char*)malloc( 4 );
+ str[0] = 'N';
+ str[1] = 'a';
+ str[2] = 'N';
+ str[3] = '\0';
+ return str;
+ }
+
+ if( format=='g' )
+ return formatGeneral( hn, prec );
+ else if( format=='f' )
+ return formatFixed( hn, prec );
+ else if( format=='e' )
+ return formatExp( hn, prec );
+
+ // fallback to 'g'
+ return formatGeneral( hn, prec );
+}
+
+HNumber HMath::pi()
+{
+ return HNumber("3.14159265358979323846264338327950288419716939937510"
+ "58209749445923078164062862089986280348253421170679"
+ "82148086513282306647093844609550582231725359408128"
+ "48111745028410270193852110555964462294895493038196"
+ "44288109756659334461284756482337867831652712019091"
+ "45648566923460348610454326648213393607260249141273"
+ "72458700660631558817488152092096282925409171536436"
+ "78925903600113305305488204665213841469519415116094"
+ "33057270365759591953092186117381932611793105118548"
+ "07446237996274956735188575272489122793818301194912"
+ "98336733624406566430860213949463952247371907021798"
+ "60943702770539217176293176752384674818467669405132"
+ "00056812714526356082778577134275778960917363717872"
+ "14684409012249534301465495853710507922796892589235"
+ "42019956112129021960864034418159813629774771309960"
+ "51870721134999999837297804995105973173281609631859"
+ "50244594553469083026425223082533446850352619311881"
+ "71010003137838752886587533208381420617177669147303"
+ "59825349042875546873115956286388235378759375195778"
+ "1857780532171226806613001927876611195909216420198" );
+}
+
+HNumber HMath::add( const HNumber& n1, const HNumber& n2 )
+{
+ HNumber result = n1 + n2;
+ return result;
+}
+
+HNumber HMath::sub( const HNumber& n1, const HNumber& n2 )
+{
+ HNumber result = n1 - n2;
+ return result;
+}
+
+HNumber HMath::mul( const HNumber& n1, const HNumber& n2 )
+{
+ HNumber result = n1 * n2;
+ return result;
+}
+
+HNumber HMath::div( const HNumber& n1, const HNumber& n2 )
+{
+ HNumber result = n1 / n2;
+ return result;
+}
+
+int HMath::compare( const HNumber& n1, const HNumber& n2 )
+{
+ if( n1.isNan() && n2.isNan() ) return 0;
+
+ HNumber delta = sub( n1, n2 );
+ delta = HMath::round( delta, HMATH_COMPARE_PREC );
+ if( delta.isZero() ) return 0;
+ else if( delta.isNegative() ) return -1;
+ return 1;
+}
+
+HNumber HMath::abs( const HNumber& n )
+{
+ HNumber r( n );
+ r.d->num->n_sign = PLUS;
+ return r;
+}
+
+HNumber HMath::negate( const HNumber& n )
+{
+ if( n.isNan() || n.isZero() )
+ return HNumber( n );
+
+ HNumber result( n );
+ result.d->num->n_sign = ( n.d->num->n_sign == PLUS ) ? MINUS : PLUS;
+ return result;
+}
+
+HNumber HMath::round( const HNumber& n, int prec )
+{
+ if( n.isNan() )
+ return HNumber::nan();
+
+ HNumber result;
+ h_destroy( result.d->num );
+ result.d->num = h_round( n.d->num, prec );
+ return result;
+}
+
+HNumber HMath::integer( const HNumber& n )
+{
+ if( n.isNan() )
+ return HNumber::nan();
+
+ if( n.isZero() )
+ return HNumber( 0 );
+
+ HNumber result;
+ h_destroy( result.d->num );
+ result.d->num = h_rescale( n.d->num, 0 );
+ return result;
+}
+
+HNumber HMath::frac( const HNumber& n )
+{
+ if( n.isNan() )
+ return HNumber::nan();
+
+ return n - integer(n);
+}
+
+HNumber HMath::sqrt( const HNumber& n )
+{
+ if( n.isNan() )
+ return HNumber::nan();
+
+ if( n.isZero() )
+ return n;
+
+ if( n.isNegative() )
+ return HNumber::nan();
+
+ // useful constant
+ HNumber half("0.5");
+
+ // Use Netwon-Raphson algorithm
+ HNumber r( 1 );
+ for( int i = 0; i < HMATH_MAX_PREC; i++ )
+ {
+ HNumber q = n / r;
+ if( r == q ) break;
+ HNumber s = r + q;
+ r = s * half;
+ }
+
+ return r;
+}
+
+HNumber HMath::raise( const HNumber& n1, int n )
+{
+ if( n1.isNan() ) return n1;
+
+ if( n1.isZero() ) return n1;
+ if( n1 == HNumber(1) ) return n1;
+ if( n == 0 ) return HNumber(1);
+ if( n == 1 ) return n1;
+
+ HNumber result = n1;
+ for( ; n > 1; n-- ) result *= n1;
+ for( ; n < 1; n++ ) result /= n1;
+
+ return result;
+}
+
+HNumber HMath::raise( const HNumber& n1, const HNumber& n2 )
+{
+ if( n1.isNan() ) return HNumber::nan();
+ if( n2.isNan() ) return HNumber::nan();
+
+ if( n1.isZero() ) return HNumber(0);
+ if( n1 == HNumber(1) ) return n1;
+ if( n2.isZero() ) return HNumber(1);
+ if( n2 == HNumber(1) ) return n1;
+
+ if( n2 == HMath::integer(n2) )
+ {
+ // Evil hack.
+ char *str = HMath::format( n2 );
+ int i = atoi(str);
+ free (str);
+
+ return HMath::raise( n1, i );
+ }
+
+ // x^y = exp( y*ln(x) )
+ HNumber result = n2 * HMath::ln(n1);
+ result = HMath::exp( result );
+
+ return result;
+}
+
+HNumber HMath::exp( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ bool negative = x.isNegative();
+ HNumber xs = HMath::abs( x );
+
+ // adjust so that x is less than 1
+ // Taylor expansion: e^x = 1 + x + x^2/2! + x^3/3! + ...
+ HNumber num = xs;
+ HNumber den = 1;
+ HNumber sum = xs + 1;
+
+ // now loop to sum the series
+ for( int i = 2; i < HMATH_MAX_PREC; i++ )
+ {
+ num *= xs;
+ den *= HNumber(i);
+ if( num.isZero() ) break;
+ HNumber s = HMath::div( num, den );
+ if( s.isZero() ) break;
+ sum += s;
+ }
+
+ HNumber result = sum;
+ if( negative )
+ result = HMath::div( HNumber(1), result );
+
+ return result;
+};
+
+HNumber HMath::ln( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ if( !x.isPositive() )
+ return HNumber::nan();
+
+ // short circuit
+ if( x == HNumber(10) )
+ return HNumber("2.30258509299404568401799145468436420760110148862877"
+ "29760333279009675726102948650438303813865953227795"
+ "49054520440916779445247118780973037711833599749301"
+ "72118016928228381938415404059160910960135436620869" );
+
+ // useful constants
+ HNumber two(2);
+ HNumber one(1);
+ HNumber half("0.5");
+
+ // adjust so that x is between 0.5 and 2.0
+ // use the fact that ln(x^2) = 2*ln(x)
+ HNumber xs( x );
+ unsigned factor = 2;
+ while( xs >= two )
+ {
+ factor *= 2;
+ xs = HMath::sqrt( xs );
+ }
+ while( xs <= half )
+ {
+ factor *= 2;
+ xs = HMath::sqrt( xs );
+ }
+
+ // Taylor expansion: ln(x) = 2(a+a^3/3+a^5/5+...)
+ // where a=(x-1)/(x+1)
+ HNumber p = xs - 1;
+ HNumber q = xs + 1;
+ HNumber a = p / q;
+ HNumber as = a*a;
+ HNumber t = a;
+ HNumber sum = a;
+
+ // loop for the series (limited to avoid nasty cases)
+ for( int i = 3; i < HMATH_MAX_PREC; i+= 2 )
+ {
+ t *= as;
+ if( t.isZero() ) break;
+ HNumber s = HMath::div( t, HNumber(i) );
+ if( s.isZero() ) break;
+ sum += s;
+ }
+
+ HNumber result = sum * HNumber( factor );
+ return result;
+}
+
+HNumber HMath::log( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ if( !x.isPositive() )
+ return HNumber::nan();
+
+ HNumber result = HMath::ln( x ) / HMath::ln(10);
+ return result;
+}
+
+// ensure angle is within 0 to 2*pi
+// useful for sin, cos
+static HNumber simplifyAngle( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ HNumber pi2 = HMath::pi() * 2;
+ HNumber nn = x / pi2;
+ HNumber xs = x - HMath::integer(nn)*pi2;
+ if( xs.isNegative() ) xs += pi2;
+
+ return xs;
+}
+
+HNumber HMath::sin( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ // short circuit
+ if( x.isZero() )
+ return x;
+
+ // adjust to small angle for speedup
+ HNumber xs = simplifyAngle( x );
+
+ // Taylor expansion: sin(x) = x - x^3/3! + x^5/5! - x^7/7! ...
+ HNumber xsq = xs*xs;
+ HNumber num = xs;
+ HNumber den = 1;
+ HNumber sum = xs;
+
+ // loop for the series (limited to avoid nasty cases)
+ for( int i = 3; i < HMATH_MAX_PREC; i+=2 )
+ {
+ num *= xsq;
+ if( num.isZero() ) break;
+ den *= HNumber(i-1);
+ den *= HNumber(i);
+ den = HMath::negate( den );
+ HNumber s = HMath::div( num, den );
+ if( s.isZero() ) break;
+ sum += s;
+ }
+
+ HNumber result = sum;
+ return result;
+}
+
+
+HNumber HMath::cos( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ // short circuit
+ if( x.isZero() )
+ return HNumber( 1 );
+
+ // adjust to small angle for speedup
+ HNumber xs = simplifyAngle( x );
+
+ // Taylor expansion: cos(x) = 1 - x^2/2! + x^4/4! - x^6/6! ...
+ HNumber xsq = xs*xs;
+ HNumber num = 1;
+ HNumber den = 1;
+ HNumber sum = 1;
+
+ // loop for the series (limited to avoid nasty cases)
+ for( int i = 2; i < HMATH_MAX_PREC; i+=2 )
+ {
+ num *= xsq;
+ if( num.isZero() ) break;
+ den *= HNumber(i-1);
+ den *= HNumber(i);
+ den = HMath::negate( den );
+ HNumber s = num / den;
+ if( s.isZero() ) break;
+ sum += s;
+ }
+
+ HNumber result = sum;
+ return result;
+}
+
+HNumber HMath::tan( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ // tan(x) = cos(x)/sin(x)
+
+ HNumber s = HMath::sin( x );
+ if( s.isZero() )
+ return s;
+
+ HNumber c = HMath::cos( x );
+ if( c.isZero() )
+ return HNumber::nan();
+
+ HNumber result = s / c;
+ return result;
+}
+
+HNumber HMath::atan( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ // useful constants
+ HNumber one("1.0");
+ HNumber c( "0.2" );
+
+ // short circuit
+ if( x == c )
+ return HNumber("0.19739555984988075837004976519479029344758510378785"
+ "21015176889402410339699782437857326978280372880441"
+ "12628118073691360104456479886794239355747565495216"
+ "30327005221074700156450155600612861855266332573187" );
+
+ if( x == one )
+ // essentially equals to HMath::pi()/4;
+ return HNumber("0.78539816339744830961566084581987572104929234984377"
+ "64552437361480769541015715522496570087063355292669"
+ "95537021628320576661773461152387645557931339852032"
+ "12027936257102567548463027638991115573723873259549" );
+
+ bool negative = x.isNegative();
+ HNumber xs = HMath::abs( x );
+
+ // adjust so that x is less than c (we choose c = 0.2)
+ // use the fact that atan(x) = atan(c) + atan((x-c)/(1+xc))
+ HNumber factor(0);
+ HNumber base(0);
+ while( xs > c )
+ {
+ base = HMath::atan( c );
+ factor += one;
+ HNumber p = xs - c;
+ HNumber q = xs * c;
+ xs = p / (q+one);
+ }
+
+ // Taylor series: atan(x) = x - x^3/3 + x^5/5 - x^7/7 + ...
+ HNumber num = xs;
+ HNumber xsq = xs*xs;
+ HNumber den = 1;
+ HNumber sum = xs;
+
+ // loop for the series (limited to avoid nasty cases)
+ for( int i = 3; i < HMATH_MAX_PREC; i+=2 )
+ {
+ num *= xsq;
+ if( num.isZero() ) break;
+ den = HNumber(i);
+ int n = (i-1)/2;
+ if( n&1 ) den = HNumber(-i);
+ HNumber s = HMath::div( num, den );
+ if( s.isZero() ) break;
+ sum += s;
+ }
+
+ HNumber result = factor*base + sum;
+ if( negative ) result = HMath::negate( result );
+ return result;
+};
+
+HNumber HMath::asin( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ // asin(x) = atan(x/sqrt(1-x*x));
+ HNumber d = HMath::sqrt( HNumber(1) - x*x );
+ if( d.isZero() )
+ {
+ HNumber result = HMath::pi()/2;
+ if( x.isNegative() )
+ result = HMath::negate( result );
+ return result;
+ }
+
+ HNumber result = HMath::atan( x / d );
+ return result;
+};
+
+HNumber HMath::acos( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ if( x.isZero() )
+ return HMath::pi()/2;
+
+ // acos(x) = atan(sqrt(1-x*x)/x);
+ HNumber n = HMath::sqrt( HNumber(1) - x*x );
+
+ HNumber result = HMath::atan( n / x );
+ return result;
+};
+
+HNumber HMath::sinh( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ // sinh(x) = 0.5*(e^x - e^(-x) )
+ HNumber result = HMath::exp(x) - HMath::exp( HMath::negate(x) );
+ result = result / 2;
+
+ return result;
+}
+
+HNumber HMath::asinh( const HNumber& x )
+{
+ HNumber one(1);
+
+ if(x.isNan())
+ return HNumber::nan();
+
+ return HMath::ln(x + HMath::sqrt(x * x + one));
+}
+
+HNumber HMath::cosh( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ // cosh(x) = 0.5*(e^x - e^(-x) )
+ HNumber result = HMath::exp(x) + HMath::exp( HMath::negate(x) );
+ result = result / 2;
+
+ return result;
+}
+
+HNumber HMath::acosh( const HNumber& x )
+{
+ HNumber one(1), zero(0);
+
+ if(x.isNan() || x < one)
+ return HNumber::nan();
+
+ // We always return the positive arc hyperbolic cosine.
+ return HMath::ln(x + HMath::sqrt(x * x - one));
+}
+
+HNumber HMath::tanh( const HNumber& x )
+{
+ if( x.isNan() )
+ return HNumber::nan();
+
+ // tanh(h) = sinh(x)/cosh(x)
+ HNumber c = HMath::cosh( x );
+ if( c.isZero() )
+ return HNumber::nan();
+
+ HNumber s = HMath::sinh( x );
+ HNumber result = s / c;
+
+ return result;
+}
+
+HNumber HMath::atanh( const HNumber& x )
+{
+ HNumber one(1), two(2);
+
+ if(x.isNan() || HMath::abs(x) >= one)
+ return HNumber::nan();
+
+ return HMath::ln((one + x) / (one - x)) / two;
+}
+
+void HMath::finalize()
+{
+ bc_free_num( &_zero_ );
+ bc_free_num( &_one_ );
+ bc_free_num( &_two_ );
+ free( _one_ );
+ free( _zero_ );
+ free( _two_ );
+ h_grabfree();
+ h_grabfree();
+ h_grabfree();
+ h_grabfree();
+}
+
+std::ostream& operator<<( std::ostream& s, HNumber num )
+{
+ char* str = HMath::formatFixed( num );
+ s << str;
+ delete[] str;
+ return s;
+}
+
+#ifdef HMATH_TEST
+
+#include <iostream>
+#include <string.h>
+
+static int hmath_total_tests = 0;
+static int hmath_failed_tests = 0;
+
+#define CHECK(x,y) check_value(__FILE__,__LINE__,#x,x,y)
+#define CHECK_FORMAT(f,p,x,y) check_format(__FILE__,__LINE__,#x,x,f,p,y)
+#define CHECK_PRECISE(x,y) check_precise(__FILE__,__LINE__,#x,x,y)
+
+static void check_value( const char *file, int line, const char* msg,
+const HNumber&n, const char* expected )
+{
+ hmath_total_tests++;
+ char* result = HMath::formatFixed( n );
+ if( strcmp( result, expected ) )
+ {
+ hmath_failed_tests++;
+ std::cout << file << "["<< line <<"]: " << msg;
+ std::cout << " Result: " << result;
+ std::cout << ", ";
+ std::cout << "Expected: " << expected;
+ std::cout << std::endl;
+ }
+ free( result );
+}
+
+static void check_format( const char *file, int line, const char* msg,
+const HNumber&n, char format, int prec, const char* expected )
+{
+ hmath_total_tests++;
+ char* result = HMath::format( n, format, prec );
+ if( strcmp( result, expected ) )
+ {
+ hmath_failed_tests++;
+ std::cout << file << "["<< line <<"]: " << msg;
+ std::cout << " Result: " << result;
+ std::cout << ", ";
+ std::cout << "Expected: " << expected;
+ std::cout << std::endl;
+ }
+ free( result );
+}
+
+static void check_precise( const char *file, int line, const char* msg,
+const HNumber&n, const char* expected )
+{
+ hmath_total_tests++;
+ char* result = HMath::formatFixed( n, 50 );
+ if( strcmp( result, expected ) )
+ {
+ hmath_failed_tests++;
+ std::cout << file << "["<< line <<"]: " << msg;
+ std::cout << " Result: " << result;
+ std::cout << ", ";
+ std::cout << "Expected: " << expected;
+ std::cout << std::endl;
+ }
+ free( result );
+}
+
+void test_create()
+{
+ CHECK( HNumber("1.0"), "1" );
+ CHECK( HNumber("2.0"), "2" );
+ CHECK( HNumber("1e-3"), "0.001" );
+
+ // too large or small
+ CHECK( HNumber("1e200"), "NaN" );
+ CHECK( HNumber("1e-200"), "NaN" );
+}
+
+void test_format()
+{
+ // fixed decimal digits
+ CHECK_FORMAT( 'f', 0, HNumber("NaN"), "NaN" );
+ CHECK_FORMAT( 'f', 0, HNumber("0"), "0" );
+ CHECK_FORMAT( 'f', 0, HNumber("1.1"), "1" );
+ CHECK_FORMAT( 'f', 1, HNumber("2.11"), "2.1" );
+ CHECK_FORMAT( 'f', 2, HNumber("3.111"), "3.11" );
+ CHECK_FORMAT( 'f', 3, HNumber("4.1111"), "4.111" );
+ CHECK_FORMAT( 'f', 2, HNumber("3.14"), "3.14" );
+ CHECK_FORMAT( 'f', 3, HNumber("3.14"), "3.140" );
+ CHECK_FORMAT( 'f', 5, HNumber("3.14"), "3.14000" );
+ CHECK_FORMAT( 'f', 7, HNumber("3.14"), "3.1400000" );
+ CHECK_FORMAT( 'f', 7, HNumber("-0.001"), "-0.0010000" );
+ CHECK_FORMAT( 'f', 8, HNumber("-0.001"), "-0.00100000" );
+ CHECK_FORMAT( 'f', 9, HNumber("-0.001"), "-0.001000000" );
+ CHECK_FORMAT( 'f', 1, HNumber("4.001"), "4.0" );
+ CHECK_FORMAT( 'f', -1, HNumber("4.000000000000000000000000000000000000000000001"), "4" );
+
+ // exponential format
+ CHECK_FORMAT( 'e', 0, HNumber("NaN"), "NaN" );
+ CHECK_FORMAT( 'e', 0, HNumber("0"), "0e0" );
+ CHECK_FORMAT( 'e', 0, HNumber("3.14"), "3e0" );
+ CHECK_FORMAT( 'e', 1, HNumber("3.14"), "3.1e0" );
+ CHECK_FORMAT( 'e', 2, HNumber("3.14"), "3.14e0" );
+ CHECK_FORMAT( 'e', 3, HNumber("3.14"), "3.140e0" );
+ CHECK_FORMAT( 'e', 5, HNumber("3.14"), "3.14000e0" );
+ CHECK_FORMAT( 'e', 7, HNumber("3.14"), "3.1400000e0" );
+ CHECK_FORMAT( 'e', 3, HNumber("-0.001"), "-1.000e-3" );
+ CHECK_FORMAT( 'e', 2, HNumber("0.0001"), "1.00e-4" );
+ CHECK_FORMAT( 'e', 2, HNumber("0.001"), "1.00e-3" );
+ CHECK_FORMAT( 'e', 2, HNumber("0.01"), "1.00e-2" );
+ CHECK_FORMAT( 'e', 2, HNumber("0.1"), "1.00e-1" );
+ CHECK_FORMAT( 'e', 2, HNumber("1"), "1.00e0" );
+ CHECK_FORMAT( 'e', 2, HNumber("10"), "1.00e1" );
+ CHECK_FORMAT( 'e', 2, HNumber("100"), "1.00e2" );
+ CHECK_FORMAT( 'e', 2, HNumber("1000"), "1.00e3" );
+ CHECK_FORMAT( 'e', 2, HNumber("10000"), "1.00e4" );
+ CHECK_FORMAT( 'e', 2, HNumber("100000"), "1.00e5" );
+ CHECK_FORMAT( 'e', 2, HNumber("1000000"), "1.00e6" );
+ CHECK_FORMAT( 'e', 2, HNumber("10000000"), "1.00e7" );
+
+ // general format
+ CHECK_FORMAT( 'g', -1, HMath::pi(), "3.14159265358979323846" );
+ CHECK_FORMAT( 'g', 3, HNumber("0"), "0.000" );
+ CHECK_FORMAT( 'g', 3, HNumber("0.000000001"), "1.000e-9" );
+ CHECK_FORMAT( 'g', 3, HNumber("0.00000001"), "1.000e-8" );
+ CHECK_FORMAT( 'g', 3, HNumber("0.0000001"), "1.000e-7" );
+ CHECK_FORMAT( 'g', 3, HNumber("0.000001"), "1.000e-6" );
+ CHECK_FORMAT( 'g', 3, HNumber("0.00001"), "1.000e-5" );
+ CHECK_FORMAT( 'g', 3, HNumber("0.0001"), "1.000e-4" );
+ CHECK_FORMAT( 'g', 3, HNumber("0.001"), "0.001" );
+ CHECK_FORMAT( 'g', 3, HNumber("0.01"), "0.010" );
+ CHECK_FORMAT( 'g', 3, HNumber("0.1"), "0.100" );
+ CHECK_FORMAT( 'g', 3, HNumber("10"), "10.000" );
+ CHECK_FORMAT( 'g', 3, HNumber("100"), "100.000" );
+ CHECK_FORMAT( 'g', 3, HNumber("1000"), "1000.000" );
+ CHECK_FORMAT( 'g', 3, HNumber("10000"), "10000.000" );
+ CHECK_FORMAT( 'g', 3, HNumber("100000"), "100000.000" );
+ CHECK_FORMAT( 'g', 3, HNumber("1000000"), "1.000e6" );
+ CHECK_FORMAT( 'g', 3, HNumber("10000000"), "1.000e7" );
+ CHECK_FORMAT( 'g', 3, HNumber("100000000"), "1.000e8" );
+ CHECK_FORMAT( 'g', 3, HNumber("1403.1977"), "1403.198" );
+ CHECK_FORMAT( 'g', 3, HNumber("2604.1980"), "2604.198" );
+ CHECK_FORMAT( 'g', 3, HNumber("2.47e4"), "24700.000" );
+
+}
+
+void test_op()
+{
+ // addition
+ CHECK( HNumber(0)+HNumber(0), "0" );
+ CHECK( HNumber(1)+HNumber(0), "1" );
+ CHECK( HNumber(1)+HNumber(1), "2" );
+ CHECK( HNumber(1)+HNumber(2), "3" );
+ CHECK( HNumber(1)+HNumber(10), "11" );
+ CHECK( HNumber(1)+HNumber(100), "101" );
+ CHECK( HNumber(1)+HNumber(1000), "1001" );
+
+ // subtraction
+ CHECK( HNumber(0)-HNumber(0), "0" );
+ CHECK( HNumber(1)-HNumber(0), "1" );
+ CHECK( HNumber(1)-HNumber(2), "-1" );
+
+ // division
+ CHECK( HNumber(1)/HNumber(2), "0.5" );
+ CHECK_PRECISE( HNumber(1)/HNumber(3), "0.33333333333333333333333333333333333333333333333333" );
+ CHECK_PRECISE( HNumber(2)/HNumber(3), "0.66666666666666666666666666666666666666666666666667" );
+ CHECK_PRECISE( HNumber(1)/HNumber(7), "0.14285714285714285714285714285714285714285714285714" );
+ CHECK_PRECISE( HNumber(2)/HNumber(7), "0.28571428571428571428571428571428571428571428571429" );
+ CHECK_PRECISE( HNumber(3)/HNumber(7), "0.42857142857142857142857142857142857142857142857143" );
+ CHECK_PRECISE( HNumber(4)/HNumber(7), "0.57142857142857142857142857142857142857142857142857" );
+ CHECK_PRECISE( HNumber(1)/HNumber(9), "0.11111111111111111111111111111111111111111111111111" );
+
+ // multiplication
+ CHECK( HNumber(0)*HNumber(0), "0" );
+ CHECK( HNumber("1.0")*HNumber("0.0"), "0" );
+ CHECK( HNumber(1)*HNumber(1), "1" );
+ CHECK( HNumber(3)*HNumber(-4), "-12" );
+ CHECK( HNumber(-2)*HNumber(5), "-10" );
+ CHECK( HNumber(6)*HNumber(7), "42" );
+ CHECK( HNumber("1.5")*HNumber("1.5"), "2.25" );
+}
+
+void test_functions()
+{
+ // pi
+ CHECK( HMath::pi(), "3.14159265358979323846" );
+ CHECK_PRECISE( HMath::pi(), "3.14159265358979323846264338327950288419716939937511" );
+
+ // abs
+ CHECK( HMath::abs("0"), "0" );
+ CHECK( HMath::abs("1"), "1" );
+ CHECK( HMath::abs("100"), "100" );
+ CHECK( HMath::abs("-100"), "100" );
+ CHECK( HMath::abs("-3.14159"), "3.14159" );
+ CHECK( HMath::abs("NaN"), "NaN" );
+
+ // round
+ CHECK( HMath::round( "3.14" ), "3" );
+ CHECK( HMath::round( "1.77" ), "2" );
+ CHECK( HMath::round( "3.14159", 4 ), "3.1416" );
+ CHECK( HMath::round( "3.14159", 3 ), "3.142" );
+ CHECK( HMath::round( "3.14159", 2 ), "3.14" );
+ CHECK( HMath::round( "3.14159", 1 ), "3.1" );
+ CHECK( HMath::round( "3.14159", 0 ), "3" );
+ CHECK( HMath::round( "-2.6041980", 4 ), "-2.6042" );
+ CHECK( HMath::round( "-2.6041980", 3 ), "-2.604" );
+ CHECK( HMath::round( "-2.6041980", 2 ), "-2.6" );
+ CHECK( HMath::round( "-2.6041980", 1 ), "-2.6" );
+ CHECK( HMath::round( "-2.6041980", 0 ), "-3" );
+ CHECK( HMath::round( "NaN" ), "NaN" );
+
+ // integer
+ CHECK( HMath::integer( "0" ), "0" );
+ CHECK( HMath::integer( "0.25" ), "0" );
+ CHECK( HMath::integer( "0.85" ), "0" );
+ CHECK( HMath::integer( "14.0377" ), "14" );
+ CHECK( HMath::integer( "-0.25" ), "0" );
+ CHECK( HMath::integer( "-0.85" ), "0" );
+ CHECK( HMath::integer( "-14.0377" ), "-14" );
+ CHECK( HMath::integer( "NaN" ), "NaN" );
+
+ // frac
+ CHECK( HMath::frac( "0" ), "0" );
+ CHECK( HMath::frac( "3.14159" ), "0.14159" );
+ CHECK( HMath::frac( "0.14159" ), "0.14159" );
+ CHECK( HMath::frac( "-3.14159" ), "-0.14159" );
+ CHECK( HMath::frac( "-0.14159" ), "-0.14159" );
+ CHECK( HMath::frac( "NaN" ), "NaN" );
+
+ // checking function 'sqrt'
+ CHECK( HMath::sqrt(1), "1" );
+ CHECK( HMath::sqrt(4), "2" );
+ CHECK( HMath::sqrt(9), "3" );
+ CHECK( HMath::sqrt(16), "4" );
+ CHECK_PRECISE( HMath::sqrt(2), "1.41421356237309504880168872420969807856967187537695" );
+ CHECK_PRECISE( HMath::sqrt(3), "1.73205080756887729352744634150587236694280525381038" );
+ CHECK_PRECISE( HMath::sqrt(5), "2.23606797749978969640917366873127623544061835961153" );
+ CHECK_PRECISE( HMath::sqrt(7), "2.64575131106459059050161575363926042571025918308245" );
+ CHECK_PRECISE( HMath::sqrt(8), "2.82842712474619009760337744841939615713934375075390" );
+ CHECK_PRECISE( HMath::sqrt(10), "3.16227766016837933199889354443271853371955513932522" );
+ CHECK_PRECISE( HMath::sqrt(11), "3.31662479035539984911493273667068668392708854558935" );
+ CHECK_PRECISE( HMath::sqrt(12), "3.46410161513775458705489268301174473388561050762076" );
+ CHECK_PRECISE( HMath::sqrt(13), "3.60555127546398929311922126747049594625129657384525" );
+ CHECK_PRECISE( HMath::sqrt(14), "3.74165738677394138558374873231654930175601980777873" );
+ CHECK_PRECISE( HMath::sqrt(15), "3.87298334620741688517926539978239961083292170529159" );
+ CHECK_PRECISE( HMath::sqrt(17), "4.12310562561766054982140985597407702514719922537362" );
+ CHECK_PRECISE( HMath::sqrt(18), "4.24264068711928514640506617262909423570901562613084" );
+ CHECK_PRECISE( HMath::sqrt(19), "4.35889894354067355223698198385961565913700392523244" );
+ CHECK_PRECISE( HMath::sqrt(20), "4.47213595499957939281834733746255247088123671922305" );
+ CHECK( HMath::sqrt("0.04"), "0.2" );
+ CHECK( HMath::sqrt("0.09"), "0.3" );
+ CHECK( HMath::sqrt("0.16"), "0.4" );
+ CHECK( HMath::sqrt(-1), "NaN" );
+ CHECK( HMath::sqrt("NaN"), "NaN" );
+
+ // raise
+ CHECK( HMath::raise(10,-3), "0.001" );
+ CHECK( HMath::raise(10,-2), "0.01" );
+ CHECK( HMath::raise(10,-1), "0.1" );
+ CHECK( HMath::raise(10,0), "1" );
+ CHECK( HMath::raise(10,1), "10" );
+ CHECK( HMath::raise(10,2), "100" );
+ CHECK( HMath::raise(10,3), "1000" );
+ CHECK( HMath::raise(10,4), "10000" );
+ CHECK( HMath::raise("2","2"), "4" );
+ CHECK( HMath::raise("3","3"), "27" );
+ CHECK( HMath::raise("4","4"), "256" );
+ CHECK_PRECISE( HMath::raise("2","0.1"), "1.07177346253629316421300632502334202290638460497756" );
+ CHECK_PRECISE( HMath::raise("2","0.2"), "1.14869835499703500679862694677792758944385088909780" );
+ CHECK_PRECISE( HMath::raise("2","0.3"), "1.23114441334491628449939306916774310987613776110082" );
+ CHECK( HMath::raise("NaN","0"), "NaN" );
+ CHECK( HMath::raise("-1","NaN"), "NaN" );
+
+ // exp
+ CHECK_PRECISE( HMath::exp("0.1"), "1.10517091807564762481170782649024666822454719473752" );
+ CHECK_PRECISE( HMath::exp("0.2"), "1.22140275816016983392107199463967417030758094152050" );
+ CHECK_PRECISE( HMath::exp("0.3"), "1.34985880757600310398374431332800733037829969735937" );
+ CHECK_PRECISE( HMath::exp("0.4"), "1.49182469764127031782485295283722228064328277393743" );
+ CHECK_PRECISE( HMath::exp("0.5"), "1.64872127070012814684865078781416357165377610071015" );
+ CHECK_PRECISE( HMath::exp("0.6"), "1.82211880039050897487536766816286451338223880854644" );
+ CHECK_PRECISE( HMath::exp("0.7"), "2.01375270747047652162454938858306527001754239414587" );
+ CHECK_PRECISE( HMath::exp("0.8"), "2.22554092849246760457953753139507675705363413504848" );
+ CHECK_PRECISE( HMath::exp("0.9"), "2.45960311115694966380012656360247069542177230644008" );
+ CHECK_PRECISE( HMath::exp("1.0"), "2.71828182845904523536028747135266249775724709369996" );
+
+ // ln
+ CHECK_PRECISE( HMath::ln("0.1"), "-2.30258509299404568401799145468436420760110148862877" );
+ CHECK_PRECISE( HMath::ln("0.2"), "-1.60943791243410037460075933322618763952560135426852" );
+ CHECK_PRECISE( HMath::ln("0.3"), "-1.20397280432593599262274621776183850295361093080602" );
+ CHECK_PRECISE( HMath::ln("0.4"), "-0.91629073187415506518352721176801107145010121990826" );
+ CHECK_PRECISE( HMath::ln("0.5"), "-0.69314718055994530941723212145817656807550013436026" );
+ CHECK_PRECISE( HMath::ln("0.6"), "-0.51082562376599068320551409630366193487811079644577" );
+ CHECK_PRECISE( HMath::ln("0.7"), "-0.35667494393873237891263871124118447796401675904691" );
+ CHECK_PRECISE( HMath::ln("0.8"), "-0.22314355131420975576629509030983450337460108554801" );
+ CHECK_PRECISE( HMath::ln("0.9"), "-0.10536051565782630122750098083931279830612037298327" );
+ CHECK_PRECISE( HMath::ln("1.0"), "0.00000000000000000000000000000000000000000000000000" );
+ CHECK_PRECISE( HMath::ln("1.1"), "0.09531017980432486004395212328076509222060536530864" );
+ CHECK_PRECISE( HMath::ln("1.2"), "0.18232155679395462621171802515451463319738933791449" );
+ CHECK_PRECISE( HMath::ln("1.3"), "0.26236426446749105203549598688095439720416645613143" );
+ CHECK_PRECISE( HMath::ln("1.4"), "0.33647223662121293050459341021699209011148337531334" );
+ CHECK_PRECISE( HMath::ln("1.5"), "0.40546510810816438197801311546434913657199042346249" );
+ CHECK_PRECISE( HMath::ln("1.6"), "0.47000362924573555365093703114834206470089904881225" );
+ CHECK_PRECISE( HMath::ln("1.7"), "0.53062825106217039623154316318876232798710152395697" );
+ CHECK_PRECISE( HMath::ln("1.8"), "0.58778666490211900818973114061886376976937976137698" );
+ CHECK_PRECISE( HMath::ln("1.9"), "0.64185388617239477599103597720348932963627777267036" );
+ CHECK_PRECISE( HMath::ln("2.0"), "0.69314718055994530941723212145817656807550013436026" );
+ CHECK_PRECISE( HMath::ln("3.0"), "1.09861228866810969139524523692252570464749055782275" );
+ CHECK_PRECISE( HMath::ln("4.0"), "1.38629436111989061883446424291635313615100026872051" );
+ CHECK_PRECISE( HMath::ln("100"), "4.60517018598809136803598290936872841520220297725755" );
+
+ // log
+ CHECK( HMath::log("1e-5"), "-5" );
+ CHECK( HMath::log("1e-4"), "-4" );
+ CHECK( HMath::log("1e-3"), "-3" );
+ CHECK( HMath::log("10"), "1" );
+ CHECK( HMath::log("100"), "2" );
+ CHECK( HMath::log("1000"), "3" );
+ CHECK( HMath::log("10000"), "4" );
+ CHECK( HMath::log("1e5"), "5" );
+ CHECK( HMath::log("1e6"), "6" );
+ CHECK( HMath::log("1e7"), "7" );
+ CHECK( HMath::log("1e8"), "8" );
+ CHECK( HMath::log("1e9"), "9" );
+ CHECK( HMath::log("1e10"), "10" );
+ CHECK( HMath::log("-1"), "NaN" );
+ CHECK( HMath::log("NaN"), "NaN" );
+
+ // sin
+ CHECK( HMath::sin( "0" ), "0" );
+ CHECK( HMath::sin( HMath::pi()/4 ), "0.7071067811865475244" );
+ CHECK( HMath::sin( HMath::pi()/3 ), "0.86602540378443864676");
+ CHECK( HMath::sin( HMath::pi()/2 ), "1" );
+ CHECK( HMath::sin( HMath::pi()/1 ), "0" );
+ CHECK( HMath::sin( HMath::pi()*2/3 ), "0.86602540378443864676");
+ CHECK( HMath::sin( HMath::pi()*4/3 ), "-0.86602540378443864676");
+ CHECK( HMath::sin( HMath::pi()*5/3 ), "-0.86602540378443864676");
+ CHECK( HMath::sin( HMath::pi()*6/3 ), "0");
+ CHECK( HMath::sin( HMath::pi()*7/3 ), "0.86602540378443864676");
+ CHECK( HMath::sin( HMath::pi()*9/3 ), "0");
+ CHECK_PRECISE( HMath::sin("0.0"), "0.00000000000000000000000000000000000000000000000000" );
+ CHECK_PRECISE( HMath::sin("0.1"), "0.09983341664682815230681419841062202698991538801798" );
+ CHECK_PRECISE( HMath::sin("0.2"), "0.19866933079506121545941262711838975037020672954021" );
+ CHECK_PRECISE( HMath::sin("0.3"), "0.29552020666133957510532074568502737367783211174262" );
+ CHECK_PRECISE( HMath::sin("0.4"), "0.38941834230865049166631175679570526459306018344396" );
+ CHECK_PRECISE( HMath::sin("0.5"), "0.47942553860420300027328793521557138808180336794060" );
+ CHECK_PRECISE( HMath::sin("0.6"), "0.56464247339503535720094544565865790710988808499415" );
+ CHECK_PRECISE( HMath::sin("0.7"), "0.64421768723769105367261435139872018306581384457369" );
+ CHECK_PRECISE( HMath::sin("0.8"), "0.71735609089952276162717461058138536619278523779142" );
+ CHECK_PRECISE( HMath::sin("0.9"), "0.78332690962748338846138231571354862314014792572031" );
+ CHECK_PRECISE( HMath::sin("1.0"), "0.84147098480789650665250232163029899962256306079837" );
+ CHECK_PRECISE( HMath::sin("2.0"), "0.90929742682568169539601986591174484270225497144789" );
+ CHECK_PRECISE( HMath::sin("3.0"), "0.14112000805986722210074480280811027984693326425227" );
+ CHECK_PRECISE( HMath::sin("4.0"), "-0.75680249530792825137263909451182909413591288733647" );
+ CHECK_PRECISE( HMath::sin("5.0"), "-0.95892427466313846889315440615599397335246154396460" );
+
+ // cos
+ CHECK( HMath::cos( "0" ), "1" );
+ CHECK( HMath::cos( HMath::pi()/4 ), "0.7071067811865475244" );
+ CHECK( HMath::cos( HMath::pi()/3 ), "0.5");
+ CHECK( HMath::cos( HMath::pi()/2 ), "0" );
+ CHECK( HMath::cos( HMath::pi()/1 ), "-1" );
+ CHECK( HMath::cos( HMath::pi()*2/3 ), "-0.5");
+ CHECK( HMath::cos( HMath::pi()*4/3 ), "-0.5");
+ CHECK( HMath::cos( HMath::pi()*5/3 ), "0.5");
+ CHECK( HMath::cos( HMath::pi()*6/3 ), "1");
+ CHECK( HMath::cos( HMath::pi()*7/3 ), "0.5");
+ CHECK( HMath::cos( HMath::pi()*9/3 ), "-1");
+ CHECK_PRECISE( HMath::cos("0.0"), "1.00000000000000000000000000000000000000000000000000" );
+ CHECK_PRECISE( HMath::cos("0.1"), "0.99500416527802576609556198780387029483857622541508" );
+ CHECK_PRECISE( HMath::cos("0.2"), "0.98006657784124163112419651674816887739352436080657" );
+ CHECK_PRECISE( HMath::cos("0.3"), "0.95533648912560601964231022756804989824421408263204" );
+ CHECK_PRECISE( HMath::cos("0.4"), "0.92106099400288508279852673205180161402585956931985" );
+ CHECK_PRECISE( HMath::cos("0.5"), "0.87758256189037271611628158260382965199164519710974" );
+ CHECK_PRECISE( HMath::cos("0.6"), "0.82533561490967829724095249895537603887809103918847" );
+ CHECK_PRECISE( HMath::cos("0.7"), "0.76484218728448842625585999019186490926821055037370" );
+ CHECK_PRECISE( HMath::cos("0.8"), "0.69670670934716542092074998164232492610178601370806" );
+ CHECK_PRECISE( HMath::cos("0.9"), "0.62160996827066445648471615140713350872176136659124" );
+ CHECK_PRECISE( HMath::cos("1.0"), "0.54030230586813971740093660744297660373231042061792" );
+ CHECK_PRECISE( HMath::cos("2.0"), "-0.41614683654714238699756822950076218976600077107554" );
+ CHECK_PRECISE( HMath::cos("3.0"), "-0.98999249660044545727157279473126130239367909661559" );
+ CHECK_PRECISE( HMath::cos("4.0"), "-0.65364362086361191463916818309775038142413359664622" );
+
+ // tan
+ CHECK( HMath::tan( HMath::pi()/4 ), "1" );
+ CHECK( HMath::tan( HMath::pi()/3 ), "1.73205080756887729353");
+ CHECK( HMath::tan( HMath::pi()/1 ), "0" );
+ CHECK_PRECISE( HMath::tan("0.0"), "0.00000000000000000000000000000000000000000000000000" );
+ CHECK_PRECISE( HMath::tan("0.1"), "0.10033467208545054505808004578111153681900480457644" );
+ CHECK_PRECISE( HMath::tan("0.2"), "0.20271003550867248332135827164753448262687566965163" );
+ CHECK_PRECISE( HMath::tan("0.3"), "0.30933624960962323303530367969829466725781590680046" );
+ CHECK_PRECISE( HMath::tan("0.4"), "0.42279321873816176198163542716529033394198977271569" );
+ CHECK_PRECISE( HMath::tan("0.5"), "0.54630248984379051325517946578028538329755172017979" );
+ CHECK_PRECISE( HMath::tan("0.6"), "0.68413680834169231707092541746333574524265408075678" );
+ CHECK_PRECISE( HMath::tan("0.7"), "0.84228838046307944812813500221293771718722125080420" );
+ CHECK_PRECISE( HMath::tan("0.8"), "1.02963855705036401274636117282036528416821960677231" );
+ CHECK_PRECISE( HMath::tan("0.9"), "1.26015821755033913713457548539574847783362583439629" );
+ CHECK_PRECISE( HMath::tan("1.0"), "1.55740772465490223050697480745836017308725077238152" );
+ CHECK_PRECISE( HMath::tan("2.0"), "-2.18503986326151899164330610231368254343201774622766" );
+ CHECK_PRECISE( HMath::tan("3.0"), "-0.14254654307427780529563541053391349322609228490180" );
+ CHECK_PRECISE( HMath::tan("4.0"), "1.15782128234957758313734241826732392311976276736714" );
+
+ // atan
+ CHECK_PRECISE( HMath::atan("0.0"), "0.00000000000000000000000000000000000000000000000000" );
+ CHECK_PRECISE( HMath::atan("0.1"), "0.09966865249116202737844611987802059024327832250431" );
+ CHECK_PRECISE( HMath::atan("0.2"), "0.19739555984988075837004976519479029344758510378785" );
+ CHECK_PRECISE( HMath::atan("0.3"), "0.29145679447786709199560462143289119350316759901207" );
+ CHECK_PRECISE( HMath::atan("0.4"), "0.38050637711236488630358791681043310449740571365810" );
+ CHECK_PRECISE( HMath::atan("0.5"), "0.46364760900080611621425623146121440202853705428612" );
+ CHECK_PRECISE( HMath::atan("0.6"), "0.54041950027058415544357836460859991013514825146259" );
+ CHECK_PRECISE( HMath::atan("1.0"), "0.78539816339744830961566084581987572104929234984378" );
+ CHECK_PRECISE( HMath::atan("-0.1"), "-0.09966865249116202737844611987802059024327832250431" );
+ CHECK_PRECISE( HMath::atan("-0.2"), "-0.19739555984988075837004976519479029344758510378785" );
+ CHECK_PRECISE( HMath::atan("-0.3"), "-0.29145679447786709199560462143289119350316759901207" );
+ CHECK_PRECISE( HMath::atan("-0.4"), "-0.38050637711236488630358791681043310449740571365810" );
+ CHECK_PRECISE( HMath::atan("-0.5"), "-0.46364760900080611621425623146121440202853705428612" );
+ CHECK_PRECISE( HMath::atan("-0.6"), "-0.54041950027058415544357836460859991013514825146259" );
+ CHECK_PRECISE( HMath::atan("-1.0"), "-0.78539816339744830961566084581987572104929234984378" );
+
+ // asin
+ CHECK_PRECISE( HMath::asin("0.0"), "0.00000000000000000000000000000000000000000000000000" );
+ CHECK_PRECISE( HMath::asin("0.1"), "0.10016742116155979634552317945269331856867597222963" );
+ CHECK_PRECISE( HMath::asin("0.2"), "0.20135792079033079145512555221762341024003808140223" );
+ CHECK_PRECISE( HMath::asin("0.3"), "0.30469265401539750797200296122752916695456003170678" );
+ CHECK_PRECISE( HMath::asin("0.4"), "0.41151684606748801938473789761733560485570113512703" );
+
+ // acos
+ CHECK_PRECISE( HMath::acos("0.1"), "1.47062890563333682288579851218705812352990872745792" );
+ CHECK_PRECISE( HMath::acos("0.2"), "1.36943840600456582777619613942212803185854661828532" );
+ CHECK_PRECISE( HMath::acos("0.3"), "1.26610367277949911125931873041222227514402466798078" );
+ CHECK_PRECISE( HMath::acos("0.4"), "1.15927948072740859984658379402241583724288356456053" );
+
+ // consistency: tan vs atan
+ CHECK( HMath::atan("0.10033467208545054505808004578111153681900480457644"), "0.1" );
+ CHECK( HMath::atan("0.20271003550867248332135827164753448262687566965163"), "0.2" );
+ CHECK( HMath::atan("0.30933624960962323303530367969829466725781590680046"), "0.3" );
+ CHECK( HMath::atan("0.42279321873816176198163542716529033394198977271569"), "0.4" );
+ CHECK( HMath::atan("0.54630248984379051325517946578028538329755172017979"), "0.5" );
+ CHECK( HMath::atan("0.68413680834169231707092541746333574524265408075678"), "0.6" );
+ CHECK( HMath::atan("0.84228838046307944812813500221293771718722125080420"), "0.7" );
+ CHECK( HMath::atan("1.02963855705036401274636117282036528416821960677231"), "0.8" );
+ CHECK( HMath::atan("1.26015821755033913713457548539574847783362583439629"), "0.9" );
+ CHECK( HMath::atan("1.55740772465490223050697480745836017308725077238152"), "1" );
+
+ // consistency: sin vs asin for small angle
+ CHECK( HMath::asin("0.09983341664682815230681419841062202698991538801798" ), "0.1");
+ CHECK( HMath::asin("0.19866933079506121545941262711838975037020672954021" ), "0.2");
+ CHECK( HMath::asin("0.29552020666133957510532074568502737367783211174262" ), "0.3");
+ CHECK( HMath::asin("0.38941834230865049166631175679570526459306018344396" ), "0.4");
+ CHECK( HMath::asin("0.47942553860420300027328793521557138808180336794060" ), "0.5");
+ CHECK( HMath::asin("0.56464247339503535720094544565865790710988808499415" ), "0.6");
+ CHECK( HMath::asin("0.64421768723769105367261435139872018306581384457369" ), "0.7");
+ CHECK( HMath::asin("0.71735609089952276162717461058138536619278523779142" ), "0.8");
+
+ // sinh
+ CHECK_PRECISE( HMath::sinh("0.1"), "0.10016675001984402582372938352190502351492091687856" );
+ CHECK_PRECISE( HMath::sinh("0.2"), "0.20133600254109398762556824301031737297449484262574" );
+ CHECK_PRECISE( HMath::sinh("0.3"), "0.30452029344714261895843526700509522909802423268018" );
+ CHECK_PRECISE( HMath::sinh("0.4"), "0.41075232580281550854021001384469810435315092436331" );
+ CHECK_PRECISE( HMath::sinh("0.5"), "0.52109530549374736162242562641149155910592898261148" );
+ CHECK_PRECISE( HMath::sinh("0.6"), "0.63665358214824127112345437546514831902496342592790" );
+ CHECK_PRECISE( HMath::sinh("0.7"), "0.75858370183953350345987464759276815415493761421703" );
+ CHECK_PRECISE( HMath::sinh("0.8"), "0.88810598218762300657471757318975698055970959688815" );
+ CHECK_PRECISE( HMath::sinh("0.9"), "1.02651672570817527595833616197842235379403446513485" );
+ CHECK_PRECISE( HMath::sinh("1.0"), "1.17520119364380145688238185059560081515571798133410" );
+
+ // cosh
+ CHECK_PRECISE( HMath::cosh("0.1"), "1.00500416805580359898797844296834164470962627785896" );
+ CHECK_PRECISE( HMath::cosh("0.2"), "1.02006675561907584629550375162935679733308609889476" );
+ CHECK_PRECISE( HMath::cosh("0.3"), "1.04533851412886048502530904632291210128027546467919" );
+ CHECK_PRECISE( HMath::cosh("0.4"), "1.08107237183845480928464293899252417629013184957412" );
+ CHECK_PRECISE( HMath::cosh("0.5"), "1.12762596520638078522622516140267201254784711809867" );
+ CHECK_PRECISE( HMath::cosh("0.6"), "1.18546521824226770375191329269771619435727538261853" );
+ CHECK_PRECISE( HMath::cosh("0.7"), "1.25516900563094301816467474099029711586260477992884" );
+ CHECK_PRECISE( HMath::cosh("0.8"), "1.33743494630484459800481995820531977649392453816033" );
+ CHECK_PRECISE( HMath::cosh("0.9"), "1.43308638544877438784179040162404834162773784130523" );
+ CHECK_PRECISE( HMath::cosh("1.0"), "1.54308063481524377847790562075706168260152911236586" );
+
+}
+
+int test_hmath()
+{
+ hmath_total_tests = 0;
+ hmath_failed_tests = 0;
+
+ test_create();
+ test_format();
+ test_op();
+ test_functions();
+
+ std::cout << hmath_total_tests << " total, ";
+ std::cout << hmath_failed_tests << " failed\n";
+
+ HMath::finalize();
+ return hmath_failed_tests;
+};
+
+#endif
+
+// vim: set et sw=2 ts=8:
diff --git a/src/hmath.h b/src/hmath.h
new file mode 100644
index 0000000..dae4749
--- /dev/null
+++ b/src/hmath.h
@@ -0,0 +1,359 @@
+/* HMath: C++ high precision math routines
+ Copyright (C) 2004 Ariya Hidayat <ariya.hidayat@gmail.com>
+
+ This file was copied from the SpeedCrunch program. Please visit
+ http://speedcrunch.berlios.de/ for more information.
+
+ 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.
+
+ */
+
+#ifndef HMATH_H
+#define HMATH_H
+
+#include <iostream>
+
+class HMath;
+
+class HNumber
+{
+friend class HMath;
+
+public:
+
+ /*!
+ * Creates a new number.
+ */
+ HNumber();
+
+ /*!
+ * Destroys this number.
+ */
+ ~HNumber();
+
+ /*!
+ * Copies from another number.
+ */
+ HNumber( const HNumber& );
+
+ /*!
+ * Assigns from another number.
+ */
+ HNumber& operator=( const HNumber& );
+
+ /*!
+ * Creates a new number from an integer value.
+ */
+ HNumber( int i );
+
+ /*!
+ * Creates a new number from a string.
+ */
+ HNumber( const char* );
+
+ /*!
+ * Returns true if this number is Not a Number (NaN).
+ */
+ bool isNan() const;
+
+ /*!
+ * Returns true if this number is zero.
+ */
+ bool isZero() const;
+
+ /*!
+ * Returns true if this number is more than zero.
+ */
+ bool isPositive() const;
+
+ /*!
+ * Returns true if this number is less than zero.
+ */
+ bool isNegative() const;
+
+ /*!
+ * Adds another number.
+ */
+ HNumber operator+( const HNumber& ) const;
+
+ /*!
+ * Adds another number.
+ */
+ HNumber& operator+=( const HNumber& );
+
+ /*!
+ * Subtract from another number.
+ */
+ HNumber operator-( const HNumber& ) const;
+
+ /*!
+ * Subtract from another number.
+ */
+ HNumber& operator-=( const HNumber& );
+
+ /*!
+ * Multiplies with another number.
+ */
+ HNumber operator*( const HNumber& ) const;
+
+ /*!
+ * Multiplies with another number.
+ */
+ HNumber& operator*=( const HNumber& );
+
+ /*!
+ * Divides with another number.
+ */
+ HNumber operator/( const HNumber& ) const;
+
+ /*!
+ * Divides with another number.
+ */
+ HNumber& operator/=( const HNumber& );
+
+ /*!
+ * Returns true if this number is greater than n.
+ */
+ bool operator>( const HNumber& n ) const;
+
+ /*!
+ * Returns true if this number is less than n.
+ */
+ bool operator<( const HNumber& n ) const;
+
+ /*!
+ * Returns true if this number is greater than or equal to n.
+ */
+ bool operator>=( const HNumber& n ) const;
+
+ /*!
+ * Returns true if this number is less than or equal to n.
+ */
+ bool operator<=( const HNumber& n ) const;
+
+ /*!
+ * Returns true if this number is equal to n.
+ */
+ bool operator==( const HNumber& n ) const;
+
+ /*!
+ * Returns true if this number is not equal to n.
+ */
+ bool operator!=( const HNumber& n ) const;
+
+ /*!
+ * Returns a NaN (Not a Number).
+ */
+ static HNumber nan();
+
+private:
+ class Private;
+ Private* d;
+};
+
+class QString;
+
+class HMath
+{
+public:
+
+ /*!
+ * Formats the given number as string, using specified decimal digits.
+ * Note that the returned string must be freed.
+ */
+ static char* format( const HNumber&n, char format = 'g', int prec = -1 );
+
+ /*!
+ * Formats the given number as string, using specified decimal digits.
+ * Note that the returned string must be freed.
+ */
+ static char* formatFixed( const HNumber&n, int prec = -1 );
+
+ /*!
+ * Formats the given number as string, in exponential format.
+ * Note that the returned string must be freed.
+ */
+ static char* formatExp( const HNumber&n, int prec = -1 );
+
+ /*!
+ * Formats the given number as string, using specified decimal digits.
+ * Note that the returned string must be freed.
+ */
+ static char* formatGeneral( const HNumber&n, int prec = -1 );
+
+ static QString formatGenString( const HNumber &n, int prec = -1 );
+
+ /*!
+ * Returns the constant pi.
+ */
+ static HNumber pi();
+
+ /*!
+ * Adds two numbers.
+ */
+ static HNumber add( const HNumber& n1, const HNumber& n2 );
+
+ /*!
+ * Subtracts two numbers.
+ */
+ static HNumber sub( const HNumber& n1, const HNumber& n2 );
+
+ /*!
+ * Multiplies two numbers.
+ */
+ static HNumber mul( const HNumber& n1, const HNumber& n2 );
+
+ /*!
+ * Divides two numbers.
+ */
+ static HNumber div( const HNumber& n1, const HNumber& n2 );
+
+ /*!
+ * Returns -1, 0, 1 if n1 is less than, equal to, or more than n2.
+ */
+ static int compare( const HNumber& n1, const HNumber& n2 );
+
+ /*!
+ * Returns the absolute value of n.
+ */
+ static HNumber abs( const HNumber& n );
+
+ /*!
+ * Returns the negative of n.
+ */
+ static HNumber negate( const HNumber& n );
+
+ /*!
+ * Returns the integer part of n.
+ */
+ static HNumber integer( const HNumber& n );
+
+ /*!
+ * Returns the fraction part of n.
+ */
+ static HNumber frac( const HNumber& n );
+
+ /*!
+ * Rounds n to the specified decimal digits.
+ */
+ static HNumber round( const HNumber&n, int prec = 0 );
+
+ /*!
+ * Returns the square root of n. If n is negative, returns NaN.
+ */
+ static HNumber sqrt( const HNumber& n );
+
+ /*!
+ * Raises n1 to an integer n.
+ */
+ static HNumber raise( const HNumber& n1, int n );
+
+ /*!
+ * Raises n1 to n2.
+ */
+ static HNumber raise( const HNumber& n1, const HNumber& n2 );
+
+ /*!
+ * Returns e raised to x.
+ */
+ static HNumber exp( const HNumber& x );
+
+ /*!
+ * Returns the natural logarithm of x.
+ * If x is non positive, returns NaN.
+ */
+ static HNumber ln( const HNumber& x );
+
+ /*!
+ * Returns the base-10 logarithm of x.
+ * If x is non positive, returns NaN.
+ */
+ static HNumber log( const HNumber& x );
+
+ /*!
+ * Returns the sine of x. Note that x must be in radians.
+ */
+ static HNumber sin( const HNumber& x );
+
+ /*!
+ * Returns the cosine of x. Note that x must be in radians.
+ */
+ static HNumber cos( const HNumber& x );
+
+ /*!
+ * Returns the tangent of x. Note that x must be in radians.
+ */
+ static HNumber tan( const HNumber& x );
+
+ /*!
+ * Returns the arc sine of x.
+ */
+ static HNumber asin( const HNumber& x );
+
+ /*!
+ * Returns the arc cosine of x.
+ */
+ static HNumber acos( const HNumber& x );
+
+ /*!
+ * Returns the arc tangent of x.
+ */
+ static HNumber atan( const HNumber& x );
+
+ /*!
+ * Returns the hyperbolic sine of x. Note that x must be in radians.
+ */
+ static HNumber sinh( const HNumber& x );
+
+ /*!
+ * Returns the arc hyperbolic sine of x. The result is in radians.
+ */
+ static HNumber asinh( const HNumber & x );
+
+ /*!
+ * Returns the hyperbolic cosine of x. Note that x must be in radians.
+ */
+ static HNumber cosh( const HNumber& x );
+
+ /*!
+ * Returns the arc hyperbolic cosine of x. The result is in radians.
+ */
+ static HNumber acosh( const HNumber & x );
+
+ /*!
+ * Returns the hyperbolic tangent of x. Note that x must be in radians.
+ */
+ static HNumber tanh( const HNumber& x );
+
+ /*!
+ * Returns the arc hyperbolic tangent of x. The result is in radians.
+ */
+ static HNumber atanh( const HNumber & x );
+
+ /*!
+ * Releases all resources. After calling this function, you can not use
+ * any other functions as well as class HNumber.
+ */
+ static void finalize();
+
+};
+
+std::ostream& operator<<( std::ostream& s, HNumber );
+
+#endif // HMATH_H
+
+// vim: set et sw=2 ts=8:
diff --git a/src/lexer.ll b/src/lexer.ll
new file mode 100644
index 0000000..8ec48e2
--- /dev/null
+++ b/src/lexer.ll
@@ -0,0 +1,223 @@
+/*
+ * lexer.ll - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+%option noyywrap
+%{
+#define YY_NO_UNPUT
+
+#include <kdebug.h>
+
+#include "node.h"
+#include "function.h"
+#include "parser_yacc.hpp"
+#include "result.h"
+
+int yyCurTokenPos;
+int yyThisTokenLength;
+
+int yyparse(void);
+%}
+
+DIGITS [0-9]+
+HEX [0-9A-Fa-f]+
+%%
+
+ /* Always skip whitespace */
+[ \t]* { yyCurTokenPos += yyThisTokenLength; yyThisTokenLength = yyleng; }
+
+ /* Power operator */
+"**" {
+ yyCurTokenPos += yyThisTokenLength;
+ yyThisTokenLength = 2;
+ return POWER;
+}
+
+"^" {
+ yyCurTokenPos += yyThisTokenLength;
+ yyThisTokenLength = 1;
+ return POWER;
+}
+
+[sS][eE][tT] {
+ yyCurTokenPos += yyThisTokenLength;
+ yyThisTokenLength = 3;
+ return SET;
+}
+
+[rR][eE][mM][oO][vV][eE] {
+ yyCurTokenPos += yyThisTokenLength;
+ yyThisTokenLength = 6;
+ return REMOVE;
+}
+
+[dD][eE][rR][iI][vV] {
+ yyCurTokenPos += yyThisTokenLength;
+ yyThisTokenLength = 5;
+ return DERIV;
+}
+
+ /* Read numbers of the form with at least the decimal point and trailing
+ * digits, such as .32, -234.45, .0, etc. Numbers are only read in the BEGIN
+ * state.
+ */
+{DIGITS}*([\.,]{DIGITS}+)(e[-+]?{DIGITS}+)? {
+ yyCurTokenPos += yyThisTokenLength;
+ yyThisTokenLength = yyleng;
+ return NUM;
+}
+
+ /* Read Hex */
+0x({HEX}+)? {
+ yyCurTokenPos += yyThisTokenLength;
+ yyThisTokenLength = yyleng;
+ return NUM;
+}
+
+ /* Read numbers with at least the integral part, such as +4234, -34e8, etc.
+ * Numbers are only read in the BEGIN state.
+ */
+{DIGITS}+([\.,]{DIGITS}*)?(e[-+]?{DIGITS}+)? {
+ yyCurTokenPos += yyThisTokenLength;
+ yyThisTokenLength = yyleng;
+ return NUM;
+}
+
+[nN][aA][nN] {
+ yyCurTokenPos += yyThisTokenLength;
+ yyThisTokenLength = yyleng;
+ return NUM;
+}
+
+[iI][nN][fF] {
+ yyCurTokenPos += yyThisTokenLength;
+ yyThisTokenLength = yyleng;
+ return NUM;
+}
+
+ /* This detects textual input, and if it isn't pre-declared by the parser (in
+ * other words, if it isn't a function), then it is returned as an identifier.
+ */
+[a-zA-Z_][a-zA-Z_0-9]* {
+ yyCurTokenPos += yyThisTokenLength;
+ yyThisTokenLength = yyleng;
+
+ if(FunctionManager::instance()->isFunction(yytext))
+ return FN;
+ else {
+ return ID;
+ }
+}
+
+ /* All other characters are returned as-is to the parser, who can accept or
+ * reject it as needed.
+ */
+. {
+ yyCurTokenPos += yyThisTokenLength;
+ yyThisTokenLength = 1;
+ return *yytext;
+}
+
+%%
+
+class Lexer::Private
+{
+public:
+ YY_BUFFER_STATE buffer;
+ int lastToken, thisToken;
+ int lastPos, thisPos;
+ QString lastTokenData, thisTokenData;
+};
+
+/* Declared in function.h, implemented here in lexer.l since this is where
+ * all the yy_*() functions and types are defined.
+ */
+Lexer::Lexer(const QString &expr) :
+ m_private(new Private)
+{
+ const char *exprString = expr.latin1();
+
+ yyCurTokenPos = 0;
+ yyThisTokenLength = 0;
+
+ m_private->buffer = yy_scan_string(exprString ? exprString : "");
+ m_private->lastToken = -1;
+ m_private->lastPos = -1;
+
+ m_private->thisToken = yylex();
+ m_private->thisTokenData = QString(yytext);
+
+ if(yyCurTokenPos != 0)
+ {
+ kdError() << "yyCurTokenPos should be 0!!\n";
+ }
+
+ m_private->thisPos = yyCurTokenPos;
+}
+
+Lexer::~Lexer()
+{
+ yy_delete_buffer(m_private->buffer);
+ delete m_private;
+}
+
+bool Lexer::hasNext() const
+{
+ return m_private->thisToken > 0;
+}
+
+int Lexer::nextType()
+{
+ m_private->lastTokenData = m_private->thisTokenData;
+ m_private->lastPos = m_private->thisPos;
+ m_private->lastToken = m_private->thisToken;
+
+ m_private->thisToken = yylex();
+ m_private->thisTokenData = QString(yytext);
+ m_private->thisPos = yyCurTokenPos;
+
+ return m_private->lastToken;
+}
+
+QString Lexer::tokenValue() const
+{
+ return m_private->lastTokenData;
+}
+
+int Lexer::tokenPos() const
+{
+ return m_private->lastPos;
+}
+
+/* Declared in function.h, implemented here in lexer.l since this is where
+ * all the yy_*() functions and types are defined.
+ */
+Abakus::number_t parseString(const char *str)
+{
+ YY_BUFFER_STATE buffer = yy_scan_string(str);
+
+ yyCurTokenPos = 0;
+ yyThisTokenLength = 0;
+
+ yyparse();
+ yy_delete_buffer(buffer);
+
+ if(Result::lastResult()->type() != Result::Value)
+ return Abakus::number_t();
+
+ return Result::lastResult()->result()->value();
+}
diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp
new file mode 100644
index 0000000..8300dbb
--- /dev/null
+++ b/src/mainwindow.cpp
@@ -0,0 +1,825 @@
+/*
+ * mainwindow.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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+#include "mainwindow.h"
+#include "abakuscommon.h"
+
+#include <kaccel.h>
+#include <kmenubar.h>
+#include <kaction.h>
+#include <kstdaction.h>
+#include <kshortcut.h>
+#include <kdebug.h>
+#include <kconfig.h>
+#include <kglobal.h>
+#include <kconfigbase.h>
+#include <kactionclasses.h>
+#include <kinputdialog.h>
+
+#include <qlayout.h>
+#include <qvbox.h>
+#include <qhbox.h>
+#include <qradiobutton.h>
+#include <qbuttongroup.h>
+#include <qsplitter.h>
+
+#include "editor.h"
+#include "evaluator.h"
+#include "function.h"
+#include "resultlistview.h"
+#include "resultlistviewtext.h"
+#include "valuemanager.h"
+#include "node.h"
+#include "rpnmuncher.h"
+#include "dcopIface.h"
+#include "abakuslistview.h"
+#include "result.h"
+
+MainWindow::MainWindow() : KMainWindow(0, "abakus-mainwindow"), m_popup(0), m_insert(false)
+{
+ m_mainSplitter = new QSplitter(this);
+ QWidget *box = new QWidget(m_mainSplitter);
+ QVBoxLayout *layout = new QVBoxLayout(box);
+ m_layout = layout;
+ layout->setSpacing(6);
+ layout->setMargin(6);
+
+ QWidget *configBox = new QWidget(box);
+ layout->addWidget(configBox);
+
+ QHBoxLayout *configLayout = new QHBoxLayout(configBox);
+
+ configLayout->addWidget(new QWidget(configBox));
+
+ QLabel *label = new QLabel(i18n("History: "), configBox);
+ label->setAlignment(AlignCenter);
+ configLayout->addWidget(label);
+
+ QButtonGroup *buttonGroup = new QButtonGroup(0);
+
+ QWidget *buttonGroupBox = new QWidget(configBox);
+ QHBoxLayout *buttonGroupLayout = new QHBoxLayout(buttonGroupBox);
+ buttonGroupLayout->addStretch(0);
+
+ configLayout->addWidget(buttonGroupBox);
+
+ m_degrees = new QRadioButton(i18n("&Degrees"), buttonGroupBox);
+ buttonGroup->insert(m_degrees);
+ buttonGroupLayout->addWidget(m_degrees);
+ slotDegrees();
+ connect(m_degrees, SIGNAL(clicked()), SLOT(slotDegrees()));
+
+ m_radians = new QRadioButton(i18n("&Radians"), buttonGroupBox);
+ buttonGroup->insert(m_radians);
+ buttonGroupLayout->addWidget(m_radians);
+ connect(m_radians, SIGNAL(clicked()), SLOT(slotRadians()));
+
+ m_history = new QVBox(box);
+ layout->addWidget(m_history);
+ m_history->setSpacing(6);
+ m_history->setMargin(0);
+
+ m_result = new ResultListView(m_history);
+ m_result->setSelectionMode(QListView::NoSelection);
+ m_result->setHScrollBarMode(ResultListView::AlwaysOff);
+ connect(m_result, SIGNAL(signalEntrySelected(const QString &)),
+ SLOT(slotEntrySelected(const QString &)));
+ connect(m_result, SIGNAL(signalResultSelected(const QString &)),
+ SLOT(slotResultSelected(const QString &)));
+
+ m_history->setStretchFactor(m_result, 1);
+ layout->setStretchFactor(m_history, 1);
+
+ QHBox *editBox = new QHBox(box);
+ layout->addWidget(editBox);
+ editBox->setSpacing(6);
+
+ m_edit = new Editor(editBox);
+ m_edit->setFocus();
+ editBox->setStretchFactor(m_edit, 1);
+
+ KPushButton *evalButton = new KPushButton(i18n("&Evaluate"), editBox);
+
+ connect(evalButton, SIGNAL(clicked()), SLOT(slotEvaluate()));
+
+ connect(m_edit, SIGNAL(returnPressed()), SLOT(slotReturnPressed()));
+ connect(m_edit, SIGNAL(textChanged()), SLOT(slotTextChanged()));
+
+ m_listSplitter = new QSplitter(Vertical, m_mainSplitter);
+ m_fnList = new FunctionListView(m_listSplitter);
+ m_fnList->addColumn("Functions");
+ m_fnList->addColumn("Value");
+
+ m_varList = new VariableListView(m_listSplitter);
+ m_varList->addColumn("Variables");
+ m_varList->addColumn("Value");
+
+ connect(FunctionManager::instance(), SIGNAL(signalFunctionAdded(const QString &)),
+ this, SLOT(slotNewFunction(const QString &)));
+ connect(FunctionManager::instance(), SIGNAL(signalFunctionRemoved(const QString &)),
+ this, SLOT(slotRemoveFunction(const QString &)));
+
+ connect(ValueManager::instance(), SIGNAL(signalValueAdded(const QString &, Abakus::number_t)),
+ this, SLOT(slotNewValue(const QString &, Abakus::number_t)));
+ connect(ValueManager::instance(), SIGNAL(signalValueChanged(const QString &, Abakus::number_t)),
+ this, SLOT(slotChangeValue(const QString &, Abakus::number_t)));
+ connect(ValueManager::instance(), SIGNAL(signalValueRemoved(const QString &)),
+ this, SLOT(slotRemoveValue(const QString &)));
+
+ setupLayout();
+
+ setCentralWidget(m_mainSplitter);
+
+#if KDE_IS_VERSION(3,4,89)
+ setupGUI(QSize(450, 400), Keys | StatusBar | Save | Create);
+#else
+ setupGUI(Keys | StatusBar | Save | Create);
+#endif
+
+ m_dcopInterface = new AbakusIface();
+}
+
+bool MainWindow::inRPNMode() const
+{
+ return action<KToggleAction>("toggleExpressionMode")->isChecked();
+}
+
+bool MainWindow::eventFilter(QObject *o, QEvent *e)
+{
+ return KMainWindow::eventFilter(o, e);
+}
+
+bool MainWindow::queryExit()
+{
+ saveConfig();
+ return KMainWindow::queryExit();
+}
+
+void MainWindow::contextMenuEvent(QContextMenuEvent *e)
+{
+ static KPopupMenu *popup = 0;
+
+ if(!popup) {
+ popup = new KPopupMenu(this);
+ action("options_show_menubar")->plug(popup);
+ }
+
+ if(!action<KToggleAction>("options_show_menubar")->isChecked())
+ popup->popup(e->globalPos());
+}
+
+void MainWindow::polish()
+{
+ KMainWindow::polish();
+ loadConfig();
+}
+
+void MainWindow::slotReturnPressed()
+{
+ QString text = m_edit->text();
+
+ text.replace("\n", "");
+
+ m_edit->appendHistory(text);
+
+ // Item to insert after
+ ResultListViewText *after = m_result->lastItem();
+
+ // Expand $foo references.
+ QString str = interpolateExpression(text, after);
+
+ QString resultVal;
+ ResultListViewText *item;
+
+ if(str.isNull())
+ return; // Error already has been posted
+
+ m_insert = false; // Assume we failed
+
+ if(inRPNMode()) {
+ // We're in RPN mode.
+ Abakus::number_t result = RPNParser::rpnParseString(str);
+
+ if(!RPNParser::wasError()) {
+ resultVal = result.toString();
+ ValueManager::instance()->setValue("ans", result);
+ m_insert = true;
+ }
+ else {
+ m_insert = false;
+ resultVal = i18n("Error: %1").arg(RPNParser::errorString());
+ }
+
+ // Skip creating list view items if in compact mode.
+ if(!m_history->isShown()) {
+ m_edit->setText(resultVal);
+ QTimer::singleShot(0, m_edit, SLOT(selectAll()));
+
+ return;
+ }
+
+ item = new ResultListViewText(m_result, str, resultVal, after, false);
+ }
+ else {
+
+ // Check to see if it's just a function name, if so, add (ans).
+ if(FunctionManager::instance()->isFunction(str))
+ str += " ans";
+
+ // Add right parentheses as needed to balance out the expression.
+ int parenLevel = getParenthesesLevel(str);
+ for(int i = 0; i < parenLevel; ++i)
+ str += ')';
+
+ Abakus::number_t result = parseString(str.latin1());
+
+ bool compact = !m_history->isShown();
+
+ switch(Result::lastResult()->type()) {
+ case Result::Value:
+ resultVal = result.toString();
+
+ ValueManager::instance()->setValue("ans", result);
+ if(!compact)
+ item = new ResultListViewText(m_result, str, result, after, false);
+
+ m_insert = true;
+ break;
+
+ case Result::Null: // OK, no result to speak of
+ resultVal = "OK";
+ if(!compact)
+ item = new ResultListViewText(m_result, str, resultVal, after, true);
+ break;
+
+ default:
+ resultVal = Result::lastResult()->message();
+ if(!compact)
+ item = new ResultListViewText(m_result, str, resultVal, after, true);
+ }
+
+ // Skip creating list view items if in compact mode.
+ if(compact) {
+ m_edit->setText(resultVal);
+ QTimer::singleShot(0, m_edit, SLOT(selectAll()));
+
+ return;
+ }
+ }
+
+ m_edit->setText(text);
+
+ m_result->setCurrentItem(item);
+ m_result->ensureItemVisible(item);
+
+ QTimer::singleShot(0, m_edit, SLOT(selectAll()));
+}
+
+void MainWindow::slotTextChanged()
+{
+ QString str = m_edit->text();
+
+ if(str.length() == 1 && m_insert) {
+ m_insert = false;
+
+ // Don't do anything if in RPN Mode.
+ if(inRPNMode())
+ return;
+
+ if(str.find(QRegExp("^[-+*/^]")) != -1) {
+ m_edit->setText("ans " + str + " ");
+ m_edit->moveCursor(QTextEdit::MoveEnd, false);
+ }
+ }
+}
+
+void MainWindow::slotEvaluate()
+{
+ slotReturnPressed();
+}
+
+void MainWindow::slotUpdateSize()
+{
+ if(m_newSize != QSize(0, 0))
+ resize(m_newSize);
+ else
+ resize(width(), minimumSize().height());
+}
+
+void MainWindow::slotDegrees()
+{
+ setTrigMode(Abakus::Degrees);
+ m_degrees->setChecked(true);
+ if(action("setDegreesMode"))
+ action<KToggleAction>("setDegreesMode")->setChecked(true);
+}
+
+void MainWindow::slotRadians()
+{
+ setTrigMode(Abakus::Radians);
+ m_radians->setChecked(true);
+ if(action("setRadiansMode"))
+ action<KToggleAction>("setRadiansMode")->setChecked(true);
+}
+
+int MainWindow::getParenthesesLevel(const QString &str)
+{
+ int level = 0;
+
+ for(unsigned i = 0; i < str.length(); ++i)
+ if(str[i] == '(')
+ ++level;
+ else if(str[i] == ')')
+ --level;
+
+ return level;
+}
+
+void MainWindow::loadConfig()
+{
+ {
+ KConfigGroup config(KGlobal::config(), "Settings");
+
+ QString mode = config.readEntry("Trigonometric mode", "Degrees");
+ if(mode == "Degrees") {
+ setTrigMode(Abakus::Degrees);
+ m_degrees->setChecked(true);
+ }
+ else {
+ setTrigMode(Abakus::Radians);
+ m_radians->setChecked(true);
+ }
+
+ bool useRPN = config.readBoolEntry("Use RPN Mode", false);
+ action<KToggleAction>("toggleExpressionMode")->setChecked(useRPN);
+
+ int precision = config.readNumEntry("Decimal Precision", -1);
+ if(precision < -1 || precision > 75)
+ precision = -1;
+
+ Abakus::m_prec = precision;
+ selectCorrectPrecisionAction();
+ }
+
+ {
+ KConfigGroup config(KGlobal::config(), "Variables");
+
+ QStringList list = config.readListEntry("Saved Variables");
+ for(QStringList::ConstIterator it = list.begin(); it != list.end(); ++it) {
+ QStringList values = QStringList::split('=', *it);
+ if(values.count() != 2) {
+ kdWarning() << "Your configuration file has somehow been corrupted!\n";
+ continue;
+ }
+
+ ValueManager::instance()->setValue(values[0], Abakus::number_t(values[1].latin1()));
+ }
+ }
+
+ {
+ KConfigGroup config(KGlobal::config(), "GUI");
+
+ bool showHistory = config.readBoolEntry("ShowHistory", true);
+ action<KToggleAction>("toggleHistoryList")->setChecked(showHistory);
+ m_history->setShown(showHistory);
+
+ bool showFunctions = config.readBoolEntry("ShowFunctions", true);
+ action<KToggleAction>("toggleFunctionList")->setChecked(showFunctions);
+ m_fnList->setShown(showFunctions);
+
+ bool showVariables = config.readBoolEntry("ShowVariables", true);
+ action<KToggleAction>("toggleVariableList")->setChecked(showVariables);
+ m_varList->setShown(showVariables);
+
+ bool compactMode = config.readBoolEntry("InCompactMode", false);
+ compactMode = compactMode || !showHistory;
+ action<KToggleAction>("toggleCompactMode")->setChecked(compactMode);
+
+ if(compactMode)
+ QTimer::singleShot(0, this, SLOT(slotToggleCompactMode()));
+ }
+
+ {
+ KConfigGroup config(KGlobal::config(), "Functions");
+
+ QStringList fnList = config.readListEntry("FunctionList");
+ for(QStringList::ConstIterator it = fnList.begin(); it != fnList.end(); ++it)
+ parseString(*it); // Run the function definitions through the parser
+ }
+
+ populateListViews();
+}
+
+void MainWindow::saveConfig()
+{
+ {
+ KConfigGroup config(KGlobal::config(), "Settings");
+
+ config.writeEntry("Trigonometric mode",
+ trigMode() == Abakus::Degrees
+ ? "Degrees"
+ : "Radians");
+
+ config.writeEntry("Use RPN Mode", inRPNMode());
+ config.writeEntry("Decimal Precision", Abakus::m_prec);
+ }
+
+ {
+ KConfigGroup config(KGlobal::config(), "Variables");
+
+ QStringList list;
+ QStringList values = ValueManager::instance()->valueNames();
+ QStringList::ConstIterator it = values.begin();
+
+ // Set precision to max for most accuracy
+ Abakus::m_prec = 75;
+
+ for(; it != values.end(); ++it) {
+ if(ValueManager::instance()->isValueReadOnly(*it))
+ continue;
+
+ list += QString("%1=%2")
+ .arg(*it)
+ .arg(ValueManager::instance()->value(*it).toString());
+ }
+
+ config.writeEntry("Saved Variables", list);
+ }
+
+ {
+ KConfigGroup config(KGlobal::config(), "GUI");
+ bool inCompactMode = action<KToggleAction>("toggleCompactMode")->isChecked();
+
+ config.writeEntry("InCompactMode", inCompactMode);
+
+ if(!inCompactMode) {
+ config.writeEntry("ShowHistory", m_history->isShown());
+ config.writeEntry("ShowFunctions", m_fnList->isShown());
+ config.writeEntry("ShowVariables", m_varList->isShown());
+ }
+ else {
+ config.writeEntry("ShowHistory", m_wasHistoryShown);
+ config.writeEntry("ShowFunctions", m_wasFnShown);
+ config.writeEntry("ShowVariables", m_wasVarShown);
+ }
+ }
+
+ {
+ KConfigGroup config(KGlobal::config(), "Functions");
+
+ FunctionManager *manager = FunctionManager::instance();
+
+ QStringList userFunctions = manager->functionList(FunctionManager::UserDefined);
+ QStringList saveList;
+
+ for(QStringList::ConstIterator it = userFunctions.begin(); it != userFunctions.end(); ++it) {
+ UnaryFunction *fn = dynamic_cast<UnaryFunction *>(manager->function(*it)->userFn->fn);
+ QString var = manager->function(*it)->userFn->varName;
+ QString expr = fn->operand()->infixString();
+
+ saveList += QString("set %1(%2) = %3").arg(*it).arg(var).arg(expr);
+ }
+
+ config.writeEntry("FunctionList", saveList);
+ }
+}
+
+void MainWindow::setupLayout()
+{
+ KActionCollection *ac = actionCollection();
+
+ KStdAction::quit(kapp, SLOT(quit()), ac);
+ KStdAction::showMenubar(this, SLOT(slotToggleMenuBar()), ac);
+
+ KToggleAction *ta = new KToggleAction(i18n("&Degrees"), SHIFT + ALT + Key_D, this, SLOT(slotDegrees()), ac, "setDegreesMode");
+ ta->setExclusiveGroup("TrigMode");
+ ta->setChecked(trigMode() == Abakus::Degrees);
+
+ ta = new KToggleAction(i18n("&Radians"), SHIFT + ALT + Key_R, this, SLOT(slotRadians()), ac, "setRadiansMode");
+ ta->setExclusiveGroup("TrigMode");
+ ta->setChecked(trigMode() == Abakus::Radians);
+
+ ta = new KToggleAction(i18n("Show &History List"), SHIFT + ALT + Key_H, this, SLOT(slotToggleHistoryList()), ac, "toggleHistoryList");
+ ta->setChecked(true);
+
+ ta = new KToggleAction(i18n("Show &Variables"), SHIFT + ALT + Key_V, this, SLOT(slotToggleVariableList()), ac, "toggleVariableList");
+ ta->setChecked(true);
+
+ ta = new KToggleAction(i18n("Show &Functions"), SHIFT + ALT + Key_F, this, SLOT(slotToggleFunctionList()), ac, "toggleFunctionList");
+ ta->setChecked(true);
+
+ ta = new KToggleAction(i18n("Activate &Compact Mode"), SHIFT + ALT + Key_C, this, SLOT(slotToggleCompactMode()), ac, "toggleCompactMode");
+ ta->setChecked(false);
+
+ ta = new KToggleAction(i18n("Use R&PN Mode"), SHIFT + ALT + Key_P, this, SLOT(slotToggleExpressionMode()), ac, "toggleExpressionMode");
+ ta->setChecked(false);
+
+ // Precision actions.
+ ta = new KToggleAction(i18n("&Automatic Precision"), 0, this, SLOT(slotPrecisionAuto()), ac, "precisionAuto");
+ ta->setExclusiveGroup("Precision");
+ ta->setChecked(true);
+
+ ta = new KToggleAction(i18n("&3 Decimal Digits"), 0, this, SLOT(slotPrecision3()), ac, "precision3");
+ ta->setExclusiveGroup("Precision");
+ ta->setChecked(false);
+
+ ta = new KToggleAction(i18n("&8 Decimal Digits"), 0, this, SLOT(slotPrecision8()), ac, "precision8");
+ ta->setExclusiveGroup("Precision");
+ ta->setChecked(false);
+
+ ta = new KToggleAction(i18n("&15 Decimal Digits"), 0, this, SLOT(slotPrecision15()), ac, "precision15");
+ ta->setExclusiveGroup("Precision");
+ ta->setChecked(false);
+
+ ta = new KToggleAction(i18n("&50 Decimal Digits"), 0, this, SLOT(slotPrecision50()), ac, "precision50");
+ ta->setExclusiveGroup("Precision");
+ ta->setChecked(false);
+
+ ta = new KToggleAction(i18n("C&ustom Precision..."), 0, this, SLOT(slotPrecisionCustom()), ac, "precisionCustom");
+ ta->setExclusiveGroup("Precision");
+ ta->setChecked(false);
+
+ new KAction(i18n("Clear &History"), "editclear", SHIFT + ALT + Key_L, m_result, SLOT(clear()), ac, "clearHistory");
+
+ new KAction(i18n("Select Editor"), "goto", Key_F6, m_edit, SLOT(setFocus()), ac, "select_edit");
+}
+
+void MainWindow::populateListViews()
+{
+ QStringList values = ValueManager::instance()->valueNames();
+
+ Abakus::number_t value = ValueManager::instance()->value("pi");
+ new ValueListViewItem(m_varList, "pi", value);
+
+ value = ValueManager::instance()->value("e");
+ new ValueListViewItem(m_varList, "e", value);
+}
+
+KAction *MainWindow::action(const char *key) const
+{
+ return actionCollection()->action(key);
+}
+
+void MainWindow::slotEntrySelected(const QString &text)
+{
+ m_edit->setText(text);
+ m_edit->moveCursor(QTextEdit::MoveEnd, false);
+}
+
+void MainWindow::slotResultSelected(const QString &text)
+{
+ m_edit->insert(text);
+}
+
+void MainWindow::slotToggleMenuBar()
+{
+ menuBar()->setShown(!menuBar()->isShown());
+}
+
+void MainWindow::slotToggleFunctionList()
+{
+ bool show = action<KToggleAction>("toggleFunctionList")->isChecked();
+ m_fnList->setShown(show);
+
+ if(!m_history->isShown()) {
+ m_history->setShown(true);
+ action<KToggleAction>("toggleHistoryList")->setChecked(true);
+ slotToggleHistoryList();
+ }
+
+ action<KToggleAction>("toggleCompactMode")->setChecked(false);
+}
+
+void MainWindow::slotToggleVariableList()
+{
+ bool show = action<KToggleAction>("toggleVariableList")->isChecked();
+ m_varList->setShown(show);
+
+ if(!m_history->isShown()) {
+ m_history->setShown(true);
+ action<KToggleAction>("toggleHistoryList")->setChecked(true);
+ slotToggleHistoryList();
+ }
+
+ action<KToggleAction>("toggleCompactMode")->setChecked(false);
+}
+
+void MainWindow::slotToggleHistoryList()
+{
+ bool show = action<KToggleAction>("toggleHistoryList")->isChecked();
+ m_history->setShown(show);
+
+ action<KToggleAction>("toggleCompactMode")->setChecked(false);
+}
+
+void MainWindow::slotNewFunction(const QString &name)
+{
+ UserFunction *userFn = FunctionManager::instance()->function(name)->userFn;
+ UnaryFunction *fn = dynamic_cast<UnaryFunction *>(userFn->fn);
+ QString fnName = QString("%1(%2)").arg(name, userFn->varName);
+ QString expr = fn->operand()->infixString();
+
+ new KListViewItem(m_fnList, fnName, expr);
+}
+
+void MainWindow::slotRemoveFunction(const QString &name)
+{
+ UserFunction *userFn = FunctionManager::instance()->function(name)->userFn;
+ QString fnName = QString("%1(%2)").arg(name, userFn->varName);
+
+ QListViewItem *item = 0;
+ while((item = m_fnList->findItem(fnName, 0)) != 0)
+ delete item;
+}
+
+void MainWindow::slotNewValue(const QString &name, Abakus::number_t value)
+{
+ new ValueListViewItem(m_varList, name, value);
+}
+
+void MainWindow::slotChangeValue(const QString &name, Abakus::number_t value)
+{
+ ValueListViewItem *item = static_cast<ValueListViewItem *>(m_varList->findItem(name, 0));
+
+ if(item)
+ item->valueChanged(value);
+}
+
+void MainWindow::slotRemoveValue(const QString &name)
+{
+ delete m_varList->findItem(name, 0);
+}
+
+void MainWindow::slotToggleCompactMode()
+{
+ if(action<KToggleAction>("toggleCompactMode")->isChecked()) {
+ m_wasFnShown = m_fnList->isShown();
+ m_wasVarShown = m_varList->isShown();
+ m_wasHistoryShown = m_history->isShown();
+
+ m_fnList->setShown(false);
+ m_varList->setShown(false);
+ m_history->setShown(false);
+
+ action<KToggleAction>("toggleFunctionList")->setChecked(false);
+ action<KToggleAction>("toggleVariableList")->setChecked(false);
+ action<KToggleAction>("toggleHistoryList")->setChecked(false);
+
+ m_oldSize = size();
+ m_newSize = QSize(0, 0);
+ QTimer::singleShot(0, this, SLOT(slotUpdateSize()));
+ }
+ else {
+ m_fnList->setShown(m_wasFnShown);
+ m_varList->setShown(m_wasVarShown);
+ m_history->setShown(m_wasHistoryShown);
+
+ action<KToggleAction>("toggleFunctionList")->setChecked(m_wasFnShown);
+ action<KToggleAction>("toggleVariableList")->setChecked(m_wasVarShown);
+ action<KToggleAction>("toggleHistoryList")->setChecked(m_wasHistoryShown);
+
+ m_newSize = m_oldSize;
+ QTimer::singleShot(0, this, SLOT(slotUpdateSize()));
+ }
+}
+
+void MainWindow::slotToggleExpressionMode()
+{
+}
+
+QString MainWindow::interpolateExpression(const QString &text, ResultListViewText *after)
+{
+ QString str(text);
+ QRegExp stackRE("\\$\\d+");
+ int pos;
+
+ while((pos = stackRE.search(str)) != -1) {
+ QString stackStr = stackRE.cap();
+ Abakus::number_t value;
+ unsigned numPos = stackStr.mid(1).toUInt();
+
+ if(!m_result->getStackValue(numPos, value)) {
+ new ResultListViewText(m_result, text, i18n("Marker %1 isn't set").arg(stackStr), after, true);
+ return QString::null;
+ }
+
+ str.replace(pos, stackStr.length(), value.toString());
+ }
+
+ return str;
+}
+
+void MainWindow::slotPrecisionAuto()
+{
+ Abakus::m_prec = -1;
+ redrawResults();
+}
+
+void MainWindow::slotPrecision3()
+{
+ Abakus::m_prec = 3;
+ redrawResults();
+}
+
+void MainWindow::slotPrecision8()
+{
+ Abakus::m_prec = 8;
+ redrawResults();
+}
+
+void MainWindow::slotPrecision15()
+{
+ Abakus::m_prec = 15;
+ redrawResults();
+}
+
+void MainWindow::slotPrecision50()
+{
+ Abakus::m_prec = 50;
+ redrawResults();
+}
+
+void MainWindow::slotPrecisionCustom()
+{
+ bool ok = false;
+ int precision = KInputDialog::getInteger(i18n("Select number of decimal digits to display"),
+ i18n("Decimal precision:"), Abakus::m_prec, 0, 75, 1, &ok, this);
+
+ if(ok) {
+ Abakus::m_prec = precision;
+ redrawResults();
+ }
+
+ selectCorrectPrecisionAction();
+}
+
+void MainWindow::redrawResults()
+{
+ QListViewItemIterator it(m_result);
+
+ while(it.current()) {
+ static_cast<ResultListViewText *>(it.current())->precisionChanged();
+ ++it;
+ }
+
+ it = QListViewItemIterator (m_varList);
+
+ while(it.current()) {
+ static_cast<ValueListViewItem *>(it.current())->valueChanged();
+ ++it;
+ }
+
+ // Because of the way we implemented the menu, it is possible to deselect
+ // every possibility, so make sure we have at least one selected.
+ selectCorrectPrecisionAction();
+}
+
+// This function selects the correct precision action based on the value of
+// Abakus::m_prec. Useful for loading settings, or for custom precision
+// selection.
+void MainWindow::selectCorrectPrecisionAction()
+{
+ switch(Abakus::m_prec) {
+ case 3:
+ action<KToggleAction>("precision3")->setChecked(true);
+ break;
+
+ case 8:
+ action<KToggleAction>("precision8")->setChecked(true);
+ break;
+
+ case 15:
+ action<KToggleAction>("precision15")->setChecked(true);
+ break;
+
+ case 50:
+ action<KToggleAction>("precision50")->setChecked(true);
+ break;
+
+ case -1:
+ action<KToggleAction>("precisionAuto")->setChecked(true);
+ break;
+
+ default:
+ action<KToggleAction>("precisionCustom")->setChecked(true);
+ }
+}
+
+#include "mainwindow.moc"
+
+// vim: set et ts=8 sw=4:
diff --git a/src/mainwindow.h b/src/mainwindow.h
new file mode 100644
index 0000000..ee24cdd
--- /dev/null
+++ b/src/mainwindow.h
@@ -0,0 +1,135 @@
+#ifndef ABAKUS_MAINWINDOW_H
+#define ABAKUS_MAINWINDOW_H
+/*
+ * mainwindow.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include <kmainwindow.h>
+
+#include "numerictypes.h"
+
+class QPoint;
+class QVBox;
+class QCheckBox;
+class QRadioButton;
+class QBoxLayout;
+class QListViewItem;
+class QSplitter;
+class QTimer;
+
+//class KComboBox;
+class Editor;
+class KPopupMenu;
+class KAction;
+class KListView;
+class ResultListView;
+class ResultListViewText;
+
+class AbakusIface;
+
+// Main window class, handles events and layout and stuff
+class MainWindow : public KMainWindow
+{
+ Q_OBJECT
+ public:
+ MainWindow();
+
+ bool inRPNMode() const;
+
+ protected:
+ virtual void contextMenuEvent(QContextMenuEvent *e);
+ virtual bool eventFilter(QObject *o, QEvent *e);
+ virtual bool queryExit();
+ virtual void polish();
+
+ private slots:
+ void slotReturnPressed();
+ void slotTextChanged();
+ void slotEvaluate();
+
+ void slotPrecisionAuto();
+ void slotPrecision3();
+ void slotPrecision8();
+ void slotPrecision15();
+ void slotPrecision50();
+ void slotPrecisionCustom();
+
+ void slotUpdateSize();
+
+ void slotDegrees();
+ void slotRadians();
+
+ void slotEntrySelected(const QString &text);
+ void slotResultSelected(const QString &text);
+
+ void slotToggleMenuBar();
+ void slotToggleFunctionList();
+ void slotToggleVariableList();
+ void slotToggleHistoryList();
+ void slotToggleCompactMode();
+ void slotToggleExpressionMode();
+
+ void slotNewValue(const QString &name, Abakus::number_t value);
+ void slotChangeValue(const QString &name, Abakus::number_t value);
+ void slotRemoveValue(const QString &name);
+
+ void slotNewFunction(const QString &name);
+ void slotRemoveFunction(const QString &name);
+
+ private:
+ int getParenthesesLevel(const QString &str);
+
+ void redrawResults();
+ void selectCorrectPrecisionAction();
+
+ void loadConfig();
+ void saveConfig();
+ void setupLayout();
+ void populateListViews();
+ QString interpolateExpression(const QString &text, ResultListViewText *after);
+
+ // Donated via JuK
+ KAction *action(const char *key) const;
+
+ template <class T> T *action(const char *key) const
+ {
+ return dynamic_cast<T *>(action(key));
+ }
+
+ private:
+ QVBox *m_history;
+ QRadioButton *m_degrees;
+ QRadioButton *m_radians;
+ Editor *m_edit;
+ KPopupMenu *m_popup;
+ ResultListView *m_result;
+ QString m_lastError;
+ QBoxLayout *m_layout;
+ KListView *m_fnList, *m_varList;
+ QSplitter *m_mainSplitter, *m_listSplitter;
+ QSize m_newSize, m_oldSize;
+
+ AbakusIface *m_dcopInterface;
+
+ bool m_wasFnShown, m_wasVarShown, m_wasHistoryShown;
+ bool m_compactMode;
+
+ bool m_insert;
+};
+
+#endif
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 <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., 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:
diff --git a/src/node.h b/src/node.h
new file mode 100644
index 0000000..e2331ab
--- /dev/null
+++ b/src/node.h
@@ -0,0 +1,219 @@
+#ifndef ABAKUS_NODE_H
+#define ABAKUS_NODE_H
+/*
+ * node.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include <qstring.h>
+
+#include "numerictypes.h"
+
+class Node;
+class Function;
+
+template <class T> class SharedPtr;
+typedef SharedPtr<Node> NodePtr;
+
+/**
+ * A class that operates on a Node. Called recursively on a node and all
+ * of its children.
+ */
+class NodeFunctor
+{
+ public:
+ virtual void operator()(const Node *node) = 0;
+ virtual ~NodeFunctor() { }
+};
+
+class Node
+{
+ public:
+ virtual ~Node() { }
+ virtual Abakus::number_t value() const = 0;
+
+ // Deletes a node only if it isn't a function, since those are
+ // typically read-only.
+ virtual void deleteNode(Node *node);
+
+ // Calls functor() on all subchildren and this.
+ virtual void applyMap(NodeFunctor &fn) const = 0;
+
+ // Returns an infix representation of the expression.
+ virtual QString infixString() const = 0;
+
+ // Returns the derivative of the node.
+ virtual Abakus::number_t derivative() const = 0;
+};
+
+class BaseFunction : public Node
+{
+ public:
+ BaseFunction(const char *name);
+
+ virtual QString name() const { return m_name; }
+ virtual const Function *function() const;
+
+ private:
+ QString m_name;
+ const Function *m_function;
+};
+
+class UnaryFunction : public BaseFunction
+{
+ public:
+ UnaryFunction(const char *name, Node *operand);
+ virtual ~UnaryFunction();
+
+ virtual Node *operand() const { return m_node; }
+ virtual void setOperand(Node *operand);
+
+ virtual void applyMap(NodeFunctor &fn) const;
+ virtual QString infixString() const;
+
+ private:
+ Node *m_node;
+};
+
+// Calculates the approximate derivative of its operand.
+class DerivativeFunction : public Node
+{
+ public:
+ DerivativeFunction(Node *operand, Node *where) :
+ m_operand(operand), m_where(where) { }
+ ~DerivativeFunction();
+
+ virtual Abakus::number_t value() const;
+
+ virtual void applyMap(NodeFunctor &fn) const;
+
+ // Returns an infix representation of the expression.
+ virtual QString infixString() const;
+
+ virtual Abakus::number_t derivative() const;
+
+ private:
+ Node *m_operand;
+ Node *m_where;
+};
+
+class UserDefinedFunction : public UnaryFunction
+{
+ public:
+ UserDefinedFunction(const char *name, Node *operand) : UnaryFunction(name, operand) { };
+
+ virtual Abakus::number_t value() const { return operand()->value(); }
+ virtual Abakus::number_t derivative() const { return operand()->derivative(); }
+};
+
+class BuiltinFunction : public UnaryFunction
+{
+ public:
+ BuiltinFunction(const char *name, Node *operand);
+
+ virtual Abakus::number_t value() const;
+ virtual Abakus::number_t derivative() const;
+};
+
+class UnaryOperator : public Node
+{
+ public:
+ typedef enum { Negation } Type;
+
+ UnaryOperator(Type type, Node *operand);
+ virtual ~UnaryOperator();
+
+ virtual void applyMap(NodeFunctor &fn) const;
+ virtual QString infixString() const;
+
+ virtual Abakus::number_t value() const;
+ virtual Abakus::number_t derivative() const;
+
+ virtual Type type() const { return m_type; }
+ virtual Node *operand() const { return m_node; }
+
+ private:
+ Type m_type;
+ Node *m_node;
+};
+
+class BinaryOperator : public Node
+{
+ public:
+ typedef enum { Addition, Subtraction, Multiplication, Division, Exponentiation } Type;
+
+ BinaryOperator(Type type, Node *left, Node *right);
+ virtual ~BinaryOperator();
+
+ virtual void applyMap(NodeFunctor &fn) const;
+ virtual QString infixString() const;
+
+ virtual Abakus::number_t value() const;
+ virtual Abakus::number_t derivative() const;
+
+ virtual Type type() const { return m_type; }
+ virtual Node *leftNode() const { return m_left; }
+ virtual Node *rightNode() const { return m_right; }
+
+ private:
+ bool isSimpleNode(Node *node) const;
+
+ Type m_type;
+ Node *m_left, *m_right;
+};
+
+class Identifier : public Node
+{
+ public:
+ Identifier(const char *name);
+
+ virtual void applyMap(NodeFunctor &fn) const;
+ virtual QString infixString() const { return name(); }
+
+ virtual Abakus::number_t value() const;
+ virtual Abakus::number_t derivative() const
+ {
+ if(name() == "x")
+ return "1";
+ else
+ return "0";
+ }
+
+ virtual QString name() const { return m_name; }
+
+ private:
+ QString m_name;
+};
+
+class NumericValue : public Node
+{
+ public:
+ NumericValue(const Abakus::number_t value) : m_value(value) { }
+
+ virtual void applyMap(NodeFunctor &fn) const { fn(this); }
+ virtual QString infixString() const;
+
+ virtual Abakus::number_t value() const { return m_value; }
+ virtual Abakus::number_t derivative() const { return "0"; }
+
+ private:
+ Abakus::number_t m_value;
+};
+
+#endif
+
+// vim: set et sw=4 ts=8:
diff --git a/src/number.c b/src/number.c
new file mode 100644
index 0000000..3ba4297
--- /dev/null
+++ b/src/number.c
@@ -0,0 +1,1809 @@
+/* number.c: Implements arbitrary precision numbers. */
+/*
+ Copyright (C) 1991, 1992, 1993, 1994, 1997, 2000 Free Software Foundation, Inc.
+
+ 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; see the file COPYING. If not, write to:
+
+ The Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330
+ Boston, MA 02110-1301 USA.
+
+
+ You may contact the author by:
+ e-mail: philnelson@acm.org
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+#include "number.h"
+
+#include <stdio.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>/* Prototypes needed for external utility routines. */
+
+#define bc_rt_warn rt_warn
+#define bc_rt_error rt_error
+#define bc_out_of_memory out_of_memory
+
+_PROTOTYPE(void rt_warn, (char *mesg ,...));
+_PROTOTYPE(void rt_error, (char *mesg ,...));
+_PROTOTYPE(void out_of_memory, (void));
+
+
+void out_of_memory(void){
+ return;
+}
+
+void rt_warn(char *mesg ,...){
+ return;
+}
+
+void rt_error(char *mesg ,...){
+ return;
+}
+
+/* Storage used for special numbers. */
+bc_num _zero_;
+bc_num _one_;
+bc_num _two_;
+
+static bc_num _bc_Free_list = NULL;
+
+/* new_num allocates a number and sets fields to known values. */
+
+bc_num
+bc_new_num (length, scale)
+ int length, scale;
+{
+ bc_num temp;
+
+ if (_bc_Free_list != NULL) {
+ temp = _bc_Free_list;
+ _bc_Free_list = temp->n_next;
+ } else {
+ temp = (bc_num) malloc (sizeof(bc_struct));
+ if (temp == NULL) bc_out_of_memory ();
+ }
+ temp->n_sign = PLUS;
+ temp->n_len = length;
+ temp->n_scale = scale;
+ temp->n_refs = 1;
+ temp->n_ptr = (char *) malloc (length+scale+1);
+ if (temp->n_ptr == NULL) bc_out_of_memory();
+ temp->n_value = temp->n_ptr;
+ memset (temp->n_ptr, 0, length+scale);
+ return temp;
+}
+
+/* "Frees" a bc_num NUM. Actually decreases reference count and only
+ frees the storage if reference count is zero. */
+
+void
+bc_free_num (num)
+ bc_num *num;
+{
+ if (*num == NULL) return;
+ (*num)->n_refs--;
+ if ((*num)->n_refs == 0) {
+ if ((*num)->n_ptr)
+ free ((*num)->n_ptr);
+ (*num)->n_next = _bc_Free_list;
+ _bc_Free_list = *num;
+ }
+ *num = NULL;
+}
+
+
+/* Intitialize the number package! */
+
+void
+bc_init_numbers ()
+{
+ _zero_ = bc_new_num (1,0);
+ _one_ = bc_new_num (1,0);
+ _one_->n_value[0] = 1;
+ _two_ = bc_new_num (1,0);
+ _two_->n_value[0] = 2;
+}
+
+
+/* Make a copy of a number! Just increments the reference count! */
+
+bc_num
+bc_copy_num (num)
+ bc_num num;
+{
+ num->n_refs++;
+ return num;
+}
+
+
+/* Initialize a number NUM by making it a copy of zero. */
+
+void
+bc_init_num (num)
+ bc_num *num;
+{
+ *num = bc_copy_num (_zero_);
+}
+
+/* For many things, we may have leading zeros in a number NUM.
+ _bc_rm_leading_zeros just moves the data "value" pointer to the
+ correct place and adjusts the length. */
+
+static void
+_bc_rm_leading_zeros (num)
+ bc_num num;
+{
+ /* We can move n_value to point to the first non zero digit! */
+ while (*num->n_value == 0 && num->n_len > 1) {
+ num->n_value++;
+ num->n_len--;
+ }
+}
+
+
+/* Compare two bc numbers. Return value is 0 if equal, -1 if N1 is less
+ than N2 and +1 if N1 is greater than N2. If USE_SIGN is false, just
+ compare the magnitudes. */
+
+static int
+_bc_do_compare (n1, n2, use_sign, ignore_last)
+ bc_num n1, n2;
+ int use_sign;
+ int ignore_last;
+{
+ char *n1ptr, *n2ptr;
+ int count;
+
+ /* First, compare signs. */
+ if (use_sign && n1->n_sign != n2->n_sign)
+ {
+ if (n1->n_sign == PLUS)
+ return (1); /* Positive N1 > Negative N2 */
+ else
+ return (-1); /* Negative N1 < Positive N1 */
+ }
+
+ /* Now compare the magnitude. */
+ if (n1->n_len != n2->n_len)
+ {
+ if (n1->n_len > n2->n_len)
+ {
+ /* Magnitude of n1 > n2. */
+ if (!use_sign || n1->n_sign == PLUS)
+ return (1);
+ else
+ return (-1);
+ }
+ else
+ {
+ /* Magnitude of n1 < n2. */
+ if (!use_sign || n1->n_sign == PLUS)
+ return (-1);
+ else
+ return (1);
+ }
+ }
+
+ /* If we get here, they have the same number of integer digits.
+ check the integer part and the equal length part of the fraction. */
+ count = n1->n_len + MIN (n1->n_scale, n2->n_scale);
+ n1ptr = n1->n_value;
+ n2ptr = n2->n_value;
+
+ while ((count > 0) && (*n1ptr == *n2ptr))
+ {
+ n1ptr++;
+ n2ptr++;
+ count--;
+ }
+ if (ignore_last && count == 1 && n1->n_scale == n2->n_scale)
+ return (0);
+ if (count != 0)
+ {
+ if (*n1ptr > *n2ptr)
+ {
+ /* Magnitude of n1 > n2. */
+ if (!use_sign || n1->n_sign == PLUS)
+ return (1);
+ else
+ return (-1);
+ }
+ else
+ {
+ /* Magnitude of n1 < n2. */
+ if (!use_sign || n1->n_sign == PLUS)
+ return (-1);
+ else
+ return (1);
+ }
+ }
+
+ /* They are equal up to the last part of the equal part of the fraction. */
+ if (n1->n_scale != n2->n_scale)
+ {
+ if (n1->n_scale > n2->n_scale)
+ {
+ for (count = n1->n_scale-n2->n_scale; count>0; count--)
+ if (*n1ptr++ != 0)
+ {
+ /* Magnitude of n1 > n2. */
+ if (!use_sign || n1->n_sign == PLUS)
+ return (1);
+ else
+ return (-1);
+ }
+ }
+ else
+ {
+ for (count = n2->n_scale-n1->n_scale; count>0; count--)
+ if (*n2ptr++ != 0)
+ {
+ /* Magnitude of n1 < n2. */
+ if (!use_sign || n1->n_sign == PLUS)
+ return (-1);
+ else
+ return (1);
+ }
+ }
+ }
+
+ /* They must be equal! */
+ return (0);
+}
+
+
+/* This is the "user callable" routine to compare numbers N1 and N2. */
+
+int
+bc_compare (n1, n2)
+ bc_num n1, n2;
+{
+ return _bc_do_compare (n1, n2, TRUE, FALSE);
+}
+
+/* In some places we need to check if the number is negative. */
+
+char
+bc_is_neg (num)
+ bc_num num;
+{
+ return num->n_sign == MINUS;
+}
+
+/* In some places we need to check if the number NUM is zero. */
+
+char
+bc_is_zero (num)
+ bc_num num;
+{
+ int count;
+ char *nptr;
+
+ /* Quick check. */
+ if (num == _zero_) return TRUE;
+
+ /* Initialize */
+ count = num->n_len + num->n_scale;
+ nptr = num->n_value;
+
+ /* The check */
+ while ((count > 0) && (*nptr++ == 0)) count--;
+
+ if (count != 0)
+ return FALSE;
+ else
+ return TRUE;
+}
+
+/* In some places we need to check if the number NUM is almost zero.
+ Specifically, all but the last digit is 0 and the last digit is 1.
+ Last digit is defined by scale. */
+
+char
+bc_is_near_zero (num, scale)
+ bc_num num;
+ int scale;
+{
+ int count;
+ char *nptr;
+
+ /* Error checking */
+ if (scale > num->n_scale)
+ scale = num->n_scale;
+
+ /* Initialize */
+ count = num->n_len + scale;
+ nptr = num->n_value;
+
+ /* The check */
+ while ((count > 0) && (*nptr++ == 0)) count--;
+
+ if (count != 0 && (count != 1 || *--nptr != 1))
+ return FALSE;
+ else
+ return TRUE;
+}
+
+
+/* Perform addition: N1 is added to N2 and the value is
+ returned. The signs of N1 and N2 are ignored.
+ SCALE_MIN is to set the minimum scale of the result. */
+
+static bc_num
+_bc_do_add (n1, n2, scale_min)
+ bc_num n1, n2;
+ int scale_min;
+{
+ bc_num sum;
+ int sum_scale, sum_digits;
+ char *n1ptr, *n2ptr, *sumptr;
+ int carry, n1bytes, n2bytes;
+ int count;
+
+ /* Prepare sum. */
+ sum_scale = MAX (n1->n_scale, n2->n_scale);
+ sum_digits = MAX (n1->n_len, n2->n_len) + 1;
+ sum = bc_new_num (sum_digits, MAX(sum_scale, scale_min));
+
+ /* Zero extra digits made by scale_min. */
+ if (scale_min > sum_scale)
+ {
+ sumptr = (char *) (sum->n_value + sum_scale + sum_digits);
+ for (count = scale_min - sum_scale; count > 0; count--)
+ *sumptr++ = 0;
+ }
+
+ /* Start with the fraction part. Initialize the pointers. */
+ n1bytes = n1->n_scale;
+ n2bytes = n2->n_scale;
+ n1ptr = (char *) (n1->n_value + n1->n_len + n1bytes - 1);
+ n2ptr = (char *) (n2->n_value + n2->n_len + n2bytes - 1);
+ sumptr = (char *) (sum->n_value + sum_scale + sum_digits - 1);
+
+ /* Add the fraction part. First copy the longer fraction.*/
+ if (n1bytes != n2bytes)
+ {
+ if (n1bytes > n2bytes)
+ while (n1bytes>n2bytes)
+ { *sumptr-- = *n1ptr--; n1bytes--;}
+ else
+ while (n2bytes>n1bytes)
+ { *sumptr-- = *n2ptr--; n2bytes--;}
+ }
+
+ /* Now add the remaining fraction part and equal size integer parts. */
+ n1bytes += n1->n_len;
+ n2bytes += n2->n_len;
+ carry = 0;
+ while ((n1bytes > 0) && (n2bytes > 0))
+ {
+ *sumptr = *n1ptr-- + *n2ptr-- + carry;
+ if (*sumptr > (BASE-1))
+ {
+ carry = 1;
+ *sumptr -= BASE;
+ }
+ else
+ carry = 0;
+ sumptr--;
+ n1bytes--;
+ n2bytes--;
+ }
+
+ /* Now add carry the longer integer part. */
+ if (n1bytes == 0)
+ { n1bytes = n2bytes; n1ptr = n2ptr; }
+ while (n1bytes-- > 0)
+ {
+ *sumptr = *n1ptr-- + carry;
+ if (*sumptr > (BASE-1))
+ {
+ carry = 1;
+ *sumptr -= BASE;
+ }
+ else
+ carry = 0;
+ sumptr--;
+ }
+
+ /* Set final carry. */
+ if (carry == 1)
+ *sumptr += 1;
+
+ /* Adjust sum and return. */
+ _bc_rm_leading_zeros (sum);
+ return sum;
+}
+
+
+/* Perform subtraction: N2 is subtracted from N1 and the value is
+ returned. The signs of N1 and N2 are ignored. Also, N1 is
+ assumed to be larger than N2. SCALE_MIN is the minimum scale
+ of the result. */
+
+static bc_num
+_bc_do_sub (n1, n2, scale_min)
+ bc_num n1, n2;
+ int scale_min;
+{
+ bc_num diff;
+ int diff_scale, diff_len;
+ int min_scale, min_len;
+ char *n1ptr, *n2ptr, *diffptr;
+ int borrow, count, val;
+
+ /* Allocate temporary storage. */
+ diff_len = MAX (n1->n_len, n2->n_len);
+ diff_scale = MAX (n1->n_scale, n2->n_scale);
+ min_len = MIN (n1->n_len, n2->n_len);
+ min_scale = MIN (n1->n_scale, n2->n_scale);
+ diff = bc_new_num (diff_len, MAX(diff_scale, scale_min));
+
+ /* Zero extra digits made by scale_min. */
+ if (scale_min > diff_scale)
+ {
+ diffptr = (char *) (diff->n_value + diff_len + diff_scale);
+ for (count = scale_min - diff_scale; count > 0; count--)
+ *diffptr++ = 0;
+ }
+
+ /* Initialize the subtract. */
+ n1ptr = (char *) (n1->n_value + n1->n_len + n1->n_scale -1);
+ n2ptr = (char *) (n2->n_value + n2->n_len + n2->n_scale -1);
+ diffptr = (char *) (diff->n_value + diff_len + diff_scale -1);
+
+ /* Subtract the numbers. */
+ borrow = 0;
+
+ /* Take care of the longer scaled number. */
+ if (n1->n_scale != min_scale)
+ {
+ /* n1 has the longer scale */
+ for (count = n1->n_scale - min_scale; count > 0; count--)
+ *diffptr-- = *n1ptr--;
+ }
+ else
+ {
+ /* n2 has the longer scale */
+ for (count = n2->n_scale - min_scale; count > 0; count--)
+ {
+ val = - *n2ptr-- - borrow;
+ if (val < 0)
+ {
+ val += BASE;
+ borrow = 1;
+ }
+ else
+ borrow = 0;
+ *diffptr-- = val;
+ }
+ }
+
+ /* Now do the equal length scale and integer parts. */
+
+ for (count = 0; count < min_len + min_scale; count++)
+ {
+ val = *n1ptr-- - *n2ptr-- - borrow;
+ if (val < 0)
+ {
+ val += BASE;
+ borrow = 1;
+ }
+ else
+ borrow = 0;
+ *diffptr-- = val;
+ }
+
+ /* If n1 has more digits then n2, we now do that subtract. */
+ if (diff_len != min_len)
+ {
+ for (count = diff_len - min_len; count > 0; count--)
+ {
+ val = *n1ptr-- - borrow;
+ if (val < 0)
+ {
+ val += BASE;
+ borrow = 1;
+ }
+ else
+ borrow = 0;
+ *diffptr-- = val;
+ }
+ }
+
+ /* Clean up and return. */
+ _bc_rm_leading_zeros (diff);
+ return diff;
+}
+
+
+/* Here is the full subtract routine that takes care of negative numbers.
+ N2 is subtracted from N1 and the result placed in RESULT. SCALE_MIN
+ is the minimum scale for the result. */
+
+void
+bc_sub (n1, n2, result, scale_min)
+ bc_num n1, n2, *result;
+ int scale_min;
+{
+ bc_num diff = NULL;
+ int cmp_res;
+ int res_scale;
+
+ if (n1->n_sign != n2->n_sign)
+ {
+ diff = _bc_do_add (n1, n2, scale_min);
+ diff->n_sign = n1->n_sign;
+ }
+ else
+ {
+ /* subtraction must be done. */
+ /* Compare magnitudes. */
+ cmp_res = _bc_do_compare (n1, n2, FALSE, FALSE);
+ switch (cmp_res)
+ {
+ case -1:
+ /* n1 is less than n2, subtract n1 from n2. */
+ diff = _bc_do_sub (n2, n1, scale_min);
+ diff->n_sign = (n2->n_sign == PLUS ? MINUS : PLUS);
+ break;
+ case 0:
+ /* They are equal! return zero! */
+ res_scale = MAX (scale_min, MAX(n1->n_scale, n2->n_scale));
+ diff = bc_new_num (1, res_scale);
+ memset (diff->n_value, 0, res_scale+1);
+ break;
+ case 1:
+ /* n2 is less than n1, subtract n2 from n1. */
+ diff = _bc_do_sub (n1, n2, scale_min);
+ diff->n_sign = n1->n_sign;
+ break;
+ }
+ }
+
+ /* Clean up and return. */
+ bc_free_num (result);
+ *result = diff;
+}
+
+
+/* Here is the full add routine that takes care of negative numbers.
+ N1 is added to N2 and the result placed into RESULT. SCALE_MIN
+ is the minimum scale for the result. */
+
+void
+bc_add (n1, n2, result, scale_min)
+ bc_num n1, n2, *result;
+ int scale_min;
+{
+ bc_num sum = NULL;
+ int cmp_res;
+ int res_scale;
+
+ if (n1->n_sign == n2->n_sign)
+ {
+ sum = _bc_do_add (n1, n2, scale_min);
+ sum->n_sign = n1->n_sign;
+ }
+ else
+ {
+ /* subtraction must be done. */
+ cmp_res = _bc_do_compare (n1, n2, FALSE, FALSE); /* Compare magnitudes. */
+ switch (cmp_res)
+ {
+ case -1:
+ /* n1 is less than n2, subtract n1 from n2. */
+ sum = _bc_do_sub (n2, n1, scale_min);
+ sum->n_sign = n2->n_sign;
+ break;
+ case 0:
+ /* They are equal! return zero with the correct scale! */
+ res_scale = MAX (scale_min, MAX(n1->n_scale, n2->n_scale));
+ sum = bc_new_num (1, res_scale);
+ memset (sum->n_value, 0, res_scale+1);
+ break;
+ case 1:
+ /* n2 is less than n1, subtract n2 from n1. */
+ sum = _bc_do_sub (n1, n2, scale_min);
+ sum->n_sign = n1->n_sign;
+ }
+ }
+
+ /* Clean up and return. */
+ bc_free_num (result);
+ *result = sum;
+}
+
+/* Recursive vs non-recursive multiply crossover ranges. */
+#if defined(MULDIGITS)
+#include "muldigits.h"
+#else
+#define MUL_BASE_DIGITS 80
+#endif
+
+int mul_base_digits = MUL_BASE_DIGITS;
+#define MUL_SMALL_DIGITS mul_base_digits/4
+
+/* Multiply utility routines */
+
+static bc_num
+new_sub_num (length, scale, value)
+ int length, scale;
+ char *value;
+{
+ bc_num temp;
+
+ if (_bc_Free_list != NULL) {
+ temp = _bc_Free_list;
+ _bc_Free_list = temp->n_next;
+ } else {
+ temp = (bc_num) malloc (sizeof(bc_struct));
+ if (temp == NULL) bc_out_of_memory ();
+ }
+ temp->n_sign = PLUS;
+ temp->n_len = length;
+ temp->n_scale = scale;
+ temp->n_refs = 1;
+ temp->n_ptr = NULL;
+ temp->n_value = value;
+ return temp;
+}
+
+static void
+_bc_simp_mul (bc_num n1, int n1len, bc_num n2, int n2len, bc_num *prod,
+ int full_scale)
+{
+ char *n1ptr, *n2ptr, *pvptr;
+ char *n1end, *n2end; /* To the end of n1 and n2. */
+ int indx, sum, prodlen;
+
+ prodlen = n1len+n2len+1;
+
+ *prod = bc_new_num (prodlen, 0);
+
+ n1end = (char *) (n1->n_value + n1len - 1);
+ n2end = (char *) (n2->n_value + n2len - 1);
+ pvptr = (char *) ((*prod)->n_value + prodlen - 1);
+ sum = 0;
+
+ /* Here is the loop... */
+ for (indx = 0; indx < prodlen-1; indx++)
+ {
+ n1ptr = (char *) (n1end - MAX(0, indx-n2len+1));
+ n2ptr = (char *) (n2end - MIN(indx, n2len-1));
+ while ((n1ptr >= n1->n_value) && (n2ptr <= n2end))
+ sum += *n1ptr-- * *n2ptr++;
+ *pvptr-- = sum % BASE;
+ sum = sum / BASE;
+ }
+ *pvptr = sum;
+}
+
+
+/* A special adder/subtractor for the recursive divide and conquer
+ multiply algorithm. Note: if sub is called, accum must
+ be larger that what is being subtracted. Also, accum and val
+ must have n_scale = 0. (e.g. they must look like integers. *) */
+static void
+_bc_shift_addsub (bc_num accum, bc_num val, int shift, int sub)
+{
+ signed char *accp, *valp;
+ int count, carry;
+
+ count = val->n_len;
+ if (val->n_value[0] == 0)
+ count--;
+ assert (accum->n_len+accum->n_scale >= shift+count);
+
+ /* Set up pointers and others */
+ accp = (signed char *)(accum->n_value +
+ accum->n_len + accum->n_scale - shift - 1);
+ valp = (signed char *)(val->n_value + val->n_len - 1);
+ carry = 0;
+
+ if (sub) {
+ /* Subtraction, carry is really borrow. */
+ while (count--) {
+ *accp -= *valp-- + carry;
+ if (*accp < 0) {
+ carry = 1;
+ *accp-- += BASE;
+ } else {
+ carry = 0;
+ accp--;
+ }
+ }
+ while (carry) {
+ *accp -= carry;
+ if (*accp < 0)
+ *accp-- += BASE;
+ else
+ carry = 0;
+ }
+ } else {
+ /* Addition */
+ while (count--) {
+ *accp += *valp-- + carry;
+ if (*accp > (BASE-1)) {
+ carry = 1;
+ *accp-- -= BASE;
+ } else {
+ carry = 0;
+ accp--;
+ }
+ }
+ while (carry) {
+ *accp += carry;
+ if (*accp > (BASE-1))
+ *accp-- -= BASE;
+ else
+ carry = 0;
+ }
+ }
+}
+
+/* Recursive divide and conquer multiply algorithm.
+ Based on
+ Let u = u0 + u1*(b^n)
+ Let v = v0 + v1*(b^n)
+ Then uv = (B^2n+B^n)*u1*v1 + B^n*(u1-u0)*(v0-v1) + (B^n+1)*u0*v0
+
+ B is the base of storage, number of digits in u1,u0 close to equal.
+*/
+static void
+_bc_rec_mul (bc_num u, int ulen, bc_num v, int vlen, bc_num *prod,
+ int full_scale)
+{
+ bc_num u0, u1, v0, v1;
+ int u0len, v0len;
+ bc_num m1, m2, m3, d1, d2;
+ int n, prodlen, m1zero;
+ int d1len, d2len;
+
+ /* Base case? */
+ if ((ulen+vlen) < mul_base_digits
+ || ulen < MUL_SMALL_DIGITS
+ || vlen < MUL_SMALL_DIGITS ) {
+ _bc_simp_mul (u, ulen, v, vlen, prod, full_scale);
+ return;
+ }
+
+ /* Calculate n -- the u and v split point in digits. */
+ n = (MAX(ulen, vlen)+1) / 2;
+
+ /* Split u and v. */
+ if (ulen < n) {
+ u1 = bc_copy_num (_zero_);
+ u0 = new_sub_num (ulen,0, u->n_value);
+ } else {
+ u1 = new_sub_num (ulen-n, 0, u->n_value);
+ u0 = new_sub_num (n, 0, u->n_value+ulen-n);
+ }
+ if (vlen < n) {
+ v1 = bc_copy_num (_zero_);
+ v0 = new_sub_num (vlen,0, v->n_value);
+ } else {
+ v1 = new_sub_num (vlen-n, 0, v->n_value);
+ v0 = new_sub_num (n, 0, v->n_value+vlen-n);
+ }
+ _bc_rm_leading_zeros (u1);
+ _bc_rm_leading_zeros (u0);
+ u0len = u0->n_len;
+ _bc_rm_leading_zeros (v1);
+ _bc_rm_leading_zeros (v0);
+ v0len = v0->n_len;
+
+ m1zero = bc_is_zero(u1) || bc_is_zero(v1);
+
+ /* Calculate sub results ... */
+
+ bc_init_num(&d1);
+ bc_init_num(&d2);
+ bc_sub (u1, u0, &d1, 0);
+ d1len = d1->n_len;
+ bc_sub (v0, v1, &d2, 0);
+ d2len = d2->n_len;
+
+
+ /* Do recursive multiplies and shifted adds. */
+ if (m1zero)
+ m1 = bc_copy_num (_zero_);
+ else
+ _bc_rec_mul (u1, u1->n_len, v1, v1->n_len, &m1, 0);
+
+ if (bc_is_zero(d1) || bc_is_zero(d2))
+ m2 = bc_copy_num (_zero_);
+ else
+ _bc_rec_mul (d1, d1len, d2, d2len, &m2, 0);
+
+ if (bc_is_zero(u0) || bc_is_zero(v0))
+ m3 = bc_copy_num (_zero_);
+ else
+ _bc_rec_mul (u0, u0->n_len, v0, v0->n_len, &m3, 0);
+
+ /* Initialize product */
+ prodlen = ulen+vlen+1;
+ *prod = bc_new_num(prodlen, 0);
+
+ if (!m1zero) {
+ _bc_shift_addsub (*prod, m1, 2*n, 0);
+ _bc_shift_addsub (*prod, m1, n, 0);
+ }
+ _bc_shift_addsub (*prod, m3, n, 0);
+ _bc_shift_addsub (*prod, m3, 0, 0);
+ _bc_shift_addsub (*prod, m2, n, d1->n_sign != d2->n_sign);
+
+ /* Now clean up! */
+ bc_free_num (&u1);
+ bc_free_num (&u0);
+ bc_free_num (&v1);
+ bc_free_num (&m1);
+ bc_free_num (&v0);
+ bc_free_num (&m2);
+ bc_free_num (&m3);
+ bc_free_num (&d1);
+ bc_free_num (&d2);
+}
+
+/* The multiply routine. N2 times N1 is put int PROD with the scale of
+ the result being MIN(N2 scale+N1 scale, MAX (SCALE, N2 scale, N1 scale)).
+ */
+
+void
+bc_multiply (n1, n2, prod, scale)
+ bc_num n1, n2, *prod;
+ int scale;
+{
+ bc_num pval;
+ int len1, len2;
+ int full_scale, prod_scale;
+
+ /* Initialize things. */
+ len1 = n1->n_len + n1->n_scale;
+ len2 = n2->n_len + n2->n_scale;
+ full_scale = n1->n_scale + n2->n_scale;
+ prod_scale = MIN(full_scale,MAX(scale,MAX(n1->n_scale,n2->n_scale)));
+
+ /* Do the multiply */
+ _bc_rec_mul (n1, len1, n2, len2, &pval, full_scale);
+
+ /* Assign to prod and clean up the number. */
+ pval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS );
+ pval->n_value = pval->n_ptr;
+ pval->n_len = len2 + len1 + 1 - full_scale;
+ pval->n_scale = prod_scale;
+ _bc_rm_leading_zeros (pval);
+ if (bc_is_zero (pval))
+ pval->n_sign = PLUS;
+ bc_free_num (prod);
+ *prod = pval;
+}
+
+/* Some utility routines for the divide: First a one digit multiply.
+ NUM (with SIZE digits) is multiplied by DIGIT and the result is
+ placed into RESULT. It is written so that NUM and RESULT can be
+ the same pointers. */
+
+static void
+_one_mult (num, size, digit, result)
+ unsigned char *num;
+ int size, digit;
+ unsigned char *result;
+{
+ int carry, value;
+ unsigned char *nptr, *rptr;
+
+ if (digit == 0)
+ memset (result, 0, size);
+ else
+ {
+ if (digit == 1)
+ memcpy (result, num, size);
+ else
+ {
+ /* Initialize */
+ nptr = (unsigned char *) (num+size-1);
+ rptr = (unsigned char *) (result+size-1);
+ carry = 0;
+
+ while (size-- > 0)
+ {
+ value = *nptr-- * digit + carry;
+ *rptr-- = value % BASE;
+ carry = value / BASE;
+ }
+
+ if (carry != 0) *rptr = carry;
+ }
+ }
+}
+
+
+/* The full division routine. This computes N1 / N2. It returns
+ 0 if the division is ok and the result is in QUOT. The number of
+ digits after the decimal point is SCALE. It returns -1 if division
+ by zero is tried. The algorithm is found in Knuth Vol 2. p237. */
+
+int
+bc_divide (n1, n2, quot, scale)
+ bc_num n1, n2, *quot;
+ int scale;
+{
+ bc_num qval;
+ unsigned char *num1, *num2;
+ unsigned char *ptr1, *ptr2, *n2ptr, *qptr;
+ int scale1, val;
+ unsigned int len1, len2, scale2, qdigits, extra, count;
+ unsigned int qdig, qguess, borrow, carry;
+ unsigned char *mval;
+ char zero;
+ unsigned int norm;
+
+ /* Test for divide by zero. */
+ if (bc_is_zero (n2)) return -1;
+
+ /* Test for divide by 1. If it is we must truncate. */
+ if (n2->n_scale == 0)
+ {
+ if (n2->n_len == 1 && *n2->n_value == 1)
+ {
+ qval = bc_new_num (n1->n_len, scale);
+ qval->n_sign = (n1->n_sign == n2->n_sign ? PLUS : MINUS);
+ memset (&qval->n_value[n1->n_len],0,scale);
+ memcpy (qval->n_value, n1->n_value,
+ n1->n_len + MIN(n1->n_scale,scale));
+ bc_free_num (quot);
+ *quot = qval;
+ }
+ }
+
+ /* Set up the divide. Move the decimal point on n1 by n2's scale.
+ Remember, zeros on the end of num2 are wasted effort for dividing. */
+ scale2 = n2->n_scale;
+ n2ptr = (unsigned char *) n2->n_value+n2->n_len+scale2-1;
+ while ((scale2 > 0) && (*n2ptr-- == 0)) scale2--;
+
+ len1 = n1->n_len + scale2;
+ scale1 = n1->n_scale - scale2;
+ if (scale1 < scale)
+ extra = scale - scale1;
+ else
+ extra = 0;
+ num1 = (unsigned char *) malloc (n1->n_len+n1->n_scale+extra+2);
+ if (num1 == NULL) bc_out_of_memory();
+ memset (num1, 0, n1->n_len+n1->n_scale+extra+2);
+ memcpy (num1+1, n1->n_value, n1->n_len+n1->n_scale);
+
+ len2 = n2->n_len + scale2;
+ num2 = (unsigned char *) malloc (len2+1);
+ if (num2 == NULL) bc_out_of_memory();
+ memcpy (num2, n2->n_value, len2);
+ *(num2+len2) = 0;
+ n2ptr = num2;
+ while (*n2ptr == 0)
+ {
+ n2ptr++;
+ len2--;
+ }
+
+ /* Calculate the number of quotient digits. */
+ if (len2 > len1+scale)
+ {
+ qdigits = scale+1;
+ zero = TRUE;
+ }
+ else
+ {
+ zero = FALSE;
+ if (len2>len1)
+ qdigits = scale+1; /* One for the zero integer part. */
+ else
+ qdigits = len1-len2+scale+1;
+ }
+
+ /* Allocate and zero the storage for the quotient. */
+ qval = bc_new_num (qdigits-scale,scale);
+ memset (qval->n_value, 0, qdigits);
+
+ /* Allocate storage for the temporary storage mval. */
+ mval = (unsigned char *) malloc (len2+1);
+ if (mval == NULL) bc_out_of_memory ();
+
+ /* Now for the full divide algorithm. */
+ if (!zero)
+ {
+ /* Normalize */
+ norm = 10 / ((int)*n2ptr + 1);
+ if (norm != 1)
+ {
+ _one_mult (num1, len1+scale1+extra+1, norm, num1);
+ _one_mult (n2ptr, len2, norm, n2ptr);
+ }
+
+ /* Initialize divide loop. */
+ qdig = 0;
+ if (len2 > len1)
+ qptr = (unsigned char *) qval->n_value+len2-len1;
+ else
+ qptr = (unsigned char *) qval->n_value;
+
+ /* Loop */
+ while (qdig <= len1+scale-len2)
+ {
+ /* Calculate the quotient digit guess. */
+ if (*n2ptr == num1[qdig])
+ qguess = 9;
+ else
+ qguess = (num1[qdig]*10 + num1[qdig+1]) / *n2ptr;
+
+ /* Test qguess. */
+ if (n2ptr[1]*qguess >
+ (num1[qdig]*10 + num1[qdig+1] - *n2ptr*qguess)*10
+ + num1[qdig+2])
+ {
+ qguess--;
+ /* And again. */
+ if (n2ptr[1]*qguess >
+ (num1[qdig]*10 + num1[qdig+1] - *n2ptr*qguess)*10
+ + num1[qdig+2])
+ qguess--;
+ }
+
+ /* Multiply and subtract. */
+ borrow = 0;
+ if (qguess != 0)
+ {
+ *mval = 0;
+ _one_mult (n2ptr, len2, qguess, mval+1);
+ ptr1 = (unsigned char *) num1+qdig+len2;
+ ptr2 = (unsigned char *) mval+len2;
+ for (count = 0; count < len2+1; count++)
+ {
+ val = (int) *ptr1 - (int) *ptr2-- - borrow;
+ if (val < 0)
+ {
+ val += 10;
+ borrow = 1;
+ }
+ else
+ borrow = 0;
+ *ptr1-- = val;
+ }
+ }
+
+ /* Test for negative result. */
+ if (borrow == 1)
+ {
+ qguess--;
+ ptr1 = (unsigned char *) num1+qdig+len2;
+ ptr2 = (unsigned char *) n2ptr+len2-1;
+ carry = 0;
+ for (count = 0; count < len2; count++)
+ {
+ val = (int) *ptr1 + (int) *ptr2-- + carry;
+ if (val > 9)
+ {
+ val -= 10;
+ carry = 1;
+ }
+ else
+ carry = 0;
+ *ptr1-- = val;
+ }
+ if (carry == 1) *ptr1 = (*ptr1 + 1) % 10;
+ }
+
+ /* We now know the quotient digit. */
+ *qptr++ = qguess;
+ qdig++;
+ }
+ }
+
+ /* Clean up and return the number. */
+ qval->n_sign = ( n1->n_sign == n2->n_sign ? PLUS : MINUS );
+ if (bc_is_zero (qval)) qval->n_sign = PLUS;
+ _bc_rm_leading_zeros (qval);
+ bc_free_num (quot);
+ *quot = qval;
+
+ /* Clean up temporary storage. */
+ free (mval);
+ free (num1);
+ free (num2);
+
+ return 0; /* Everything is OK. */
+}
+
+
+/* Division *and* modulo for numbers. This computes both NUM1 / NUM2 and
+ NUM1 % NUM2 and puts the results in QUOT and REM, except that if QUOT
+ is NULL then that store will be omitted.
+ */
+
+int
+bc_divmod (num1, num2, quot, rem, scale)
+ bc_num num1, num2, *quot, *rem;
+ int scale;
+{
+ bc_num quotient = NULL;
+ bc_num temp;
+ int rscale;
+
+ /* Check for correct numbers. */
+ if (bc_is_zero (num2)) return -1;
+
+ /* Calculate final scale. */
+ rscale = MAX (num1->n_scale, num2->n_scale+scale);
+ bc_init_num(&temp);
+
+ /* Calculate it. */
+ bc_divide (num1, num2, &temp, scale);
+ if (quot)
+ quotient = bc_copy_num (temp);
+ bc_multiply (temp, num2, &temp, rscale);
+ bc_sub (num1, temp, rem, rscale);
+ bc_free_num (&temp);
+
+ if (quot)
+ {
+ bc_free_num (quot);
+ *quot = quotient;
+ }
+
+ return 0; /* Everything is OK. */
+}
+
+
+/* Modulo for numbers. This computes NUM1 % NUM2 and puts the
+ result in RESULT. */
+
+int
+bc_modulo (num1, num2, result, scale)
+ bc_num num1, num2, *result;
+ int scale;
+{
+ return bc_divmod (num1, num2, NULL, result, scale);
+}
+
+/* Raise BASE to the EXPO power, reduced modulo MOD. The result is
+ placed in RESULT. If a EXPO is not an integer,
+ only the integer part is used. */
+
+int
+bc_raisemod (base, expo, mod, result, scale)
+ bc_num base, expo, mod, *result;
+ int scale;
+{
+ bc_num power, exponent, parity, temp;
+ int rscale;
+
+ /* Check for correct numbers. */
+ if (bc_is_zero(mod)) return -1;
+ if (bc_is_neg(expo)) return -1;
+
+ /* Set initial values. */
+ power = bc_copy_num (base);
+ exponent = bc_copy_num (expo);
+ temp = bc_copy_num (_one_);
+ bc_init_num(&parity);
+
+ /* Check the base for scale digits. */
+ if (base->n_scale != 0)
+ bc_rt_warn ("non-zero scale in base");
+
+ /* Check the exponent for scale digits. */
+ if (exponent->n_scale != 0)
+ {
+ bc_rt_warn ("non-zero scale in exponent");
+ bc_divide (exponent, _one_, &exponent, 0); /*truncate */
+ }
+
+ /* Check the modulus for scale digits. */
+ if (mod->n_scale != 0)
+ bc_rt_warn ("non-zero scale in modulus");
+
+ /* Do the calculation. */
+ rscale = MAX(scale, base->n_scale);
+ while ( !bc_is_zero(exponent) )
+ {
+ (void) bc_divmod (exponent, _two_, &exponent, &parity, 0);
+ if ( !bc_is_zero(parity) )
+ {
+ bc_multiply (temp, power, &temp, rscale);
+ (void) bc_modulo (temp, mod, &temp, scale);
+ }
+
+ bc_multiply (power, power, &power, rscale);
+ (void) bc_modulo (power, mod, &power, scale);
+ }
+
+ /* Assign the value. */
+ bc_free_num (&power);
+ bc_free_num (&exponent);
+ bc_free_num (result);
+ *result = temp;
+ return 0; /* Everything is OK. */
+}
+
+/* Raise NUM1 to the NUM2 power. The result is placed in RESULT.
+ Maximum exponent is LONG_MAX. If a NUM2 is not an integer,
+ only the integer part is used. */
+
+void
+bc_raise (num1, num2, result, scale)
+ bc_num num1, num2, *result;
+ int scale;
+{
+ bc_num temp, power;
+ long exponent;
+ int rscale;
+ int pwrscale;
+ int calcscale;
+ char neg;
+
+ /* Check the exponent for scale digits and convert to a long. */
+ if (num2->n_scale != 0)
+ bc_rt_warn ("non-zero scale in exponent");
+ exponent = bc_num2long (num2);
+ if (exponent == 0 && (num2->n_len > 1 || num2->n_value[0] != 0))
+ bc_rt_error ("exponent too large in raise");
+
+ /* Special case if exponent is a zero. */
+ if (exponent == 0)
+ {
+ bc_free_num (result);
+ *result = bc_copy_num (_one_);
+ return;
+ }
+
+ /* Other initializations. */
+ if (exponent < 0)
+ {
+ neg = TRUE;
+ exponent = -exponent;
+ rscale = scale;
+ }
+ else
+ {
+ neg = FALSE;
+ rscale = MIN (num1->n_scale*exponent, MAX(scale, num1->n_scale));
+ }
+
+ /* Set initial value of temp. */
+ power = bc_copy_num (num1);
+ pwrscale = num1->n_scale;
+ while ((exponent & 1) == 0)
+ {
+ pwrscale = 2*pwrscale;
+ bc_multiply (power, power, &power, pwrscale);
+ exponent = exponent >> 1;
+ }
+ temp = bc_copy_num (power);
+ calcscale = pwrscale;
+ exponent = exponent >> 1;
+
+ /* Do the calculation. */
+ while (exponent > 0)
+ {
+ pwrscale = 2*pwrscale;
+ bc_multiply (power, power, &power, pwrscale);
+ if ((exponent & 1) == 1) {
+ calcscale = pwrscale + calcscale;
+ bc_multiply (temp, power, &temp, calcscale);
+ }
+ exponent = exponent >> 1;
+ }
+
+ /* Assign the value. */
+ if (neg)
+ {
+ bc_divide (_one_, temp, result, rscale);
+ bc_free_num (&temp);
+ }
+ else
+ {
+ bc_free_num (result);
+ *result = temp;
+ if ((*result)->n_scale > rscale)
+ (*result)->n_scale = rscale;
+ }
+ bc_free_num (&power);
+}
+
+/* Take the square root NUM and return it in NUM with SCALE digits
+ after the decimal place. */
+
+int
+bc_sqrt (num, scale)
+ bc_num *num;
+ int scale;
+{
+ int rscale, cmp_res, done;
+ int cscale;
+ bc_num guess, guess1, point5, diff;
+
+ /* Initial checks. */
+ cmp_res = bc_compare (*num, _zero_);
+ if (cmp_res < 0)
+ return 0; /* error */
+ else
+ {
+ if (cmp_res == 0)
+ {
+ bc_free_num (num);
+ *num = bc_copy_num (_zero_);
+ return 1;
+ }
+ }
+ cmp_res = bc_compare (*num, _one_);
+ if (cmp_res == 0)
+ {
+ bc_free_num (num);
+ *num = bc_copy_num (_one_);
+ return 1;
+ }
+
+ /* Initialize the variables. */
+ rscale = MAX (scale, (*num)->n_scale);
+ bc_init_num(&guess);
+ bc_init_num(&guess1);
+ bc_init_num(&diff);
+ point5 = bc_new_num (1,1);
+ point5->n_value[1] = 5;
+
+
+ /* Calculate the initial guess. */
+ if (cmp_res < 0)
+ {
+ /* The number is between 0 and 1. Guess should start at 1. */
+ guess = bc_copy_num (_one_);
+ cscale = (*num)->n_scale;
+ }
+ else
+ {
+ /* The number is greater than 1. Guess should start at 10^(exp/2). */
+ bc_int2num (&guess,10);
+
+ bc_int2num (&guess1,(*num)->n_len);
+ bc_multiply (guess1, point5, &guess1, 0);
+ guess1->n_scale = 0;
+ bc_raise (guess, guess1, &guess, 0);
+ bc_free_num (&guess1);
+ cscale = 3;
+ }
+
+ /* Find the square root using Newton's algorithm. */
+ done = FALSE;
+ while (!done)
+ {
+ bc_free_num (&guess1);
+ guess1 = bc_copy_num (guess);
+ bc_divide (*num, guess, &guess, cscale);
+ bc_add (guess, guess1, &guess, 0);
+ bc_multiply (guess, point5, &guess, cscale);
+ bc_sub (guess, guess1, &diff, cscale+1);
+ if (bc_is_near_zero (diff, cscale))
+ {
+ if (cscale < rscale+1)
+ cscale = MIN (cscale*3, rscale+1);
+ else
+ done = TRUE;
+ }
+ }
+
+ /* Assign the number and clean up. */
+ bc_free_num (num);
+ bc_divide (guess,_one_,num,rscale);
+ bc_free_num (&guess);
+ bc_free_num (&guess1);
+ bc_free_num (&point5);
+ bc_free_num (&diff);
+ return 1;
+}
+
+
+/* The following routines provide output for bcd numbers package
+ using the rules of POSIX bc for output. */
+
+/* This structure is used for saving digits in the conversion process. */
+typedef struct stk_rec {
+ long digit;
+ struct stk_rec *next;
+} stk_rec;
+
+/* The reference string for digits. */
+static char ref_str[] = "0123456789ABCDEF";
+
+
+/* A special output routine for "multi-character digits." Exactly
+ SIZE characters must be output for the value VAL. If SPACE is
+ non-zero, we must output one space before the number. OUT_CHAR
+ is the actual routine for writing the characters. */
+
+void
+bc_out_long (val, size, space, out_char)
+ long val;
+ int size, space;
+#ifdef NUMBER__STDC__
+ void (*out_char)(int);
+#else
+ void (*out_char)();
+#endif
+{
+ char digits[40];
+ int len, ix;
+
+ if (space) (*out_char) (' ');
+ sprintf (digits, "%ld", val);
+ len = strlen (digits);
+ while (size > len)
+ {
+ (*out_char) ('0');
+ size--;
+ }
+ for (ix=0; ix < len; ix++)
+ (*out_char) (digits[ix]);
+}
+
+/* Output of a bcd number. NUM is written in base O_BASE using OUT_CHAR
+ as the routine to do the actual output of the characters. */
+
+void
+bc_out_num (num, o_base, out_char, leading_zero)
+ bc_num num;
+ int o_base;
+#ifdef NUMBER__STDC__
+ void (*out_char)(int);
+#else
+ void (*out_char)();
+#endif
+ int leading_zero;
+{
+ char *nptr;
+ int index, fdigit, pre_space;
+ stk_rec *digits, *temp;
+ bc_num int_part, frac_part, base, cur_dig, t_num, max_o_digit;
+
+ /* The negative sign if needed. */
+ if (num->n_sign == MINUS) (*out_char) ('-');
+
+ /* Output the number. */
+ if (bc_is_zero (num))
+ (*out_char) ('0');
+ else
+ if (o_base == 10)
+ {
+ /* The number is in base 10, do it the fast way. */
+ nptr = num->n_value;
+ if (num->n_len > 1 || *nptr != 0)
+ for (index=num->n_len; index>0; index--)
+ (*out_char) (BCD_CHAR(*nptr++));
+ else
+ nptr++;
+
+ if (leading_zero && bc_is_zero (num))
+ (*out_char) ('0');
+
+ /* Now the fraction. */
+ if (num->n_scale > 0)
+ {
+ (*out_char) ('.');
+ for (index=0; index<num->n_scale; index++)
+ (*out_char) (BCD_CHAR(*nptr++));
+ }
+ }
+ else
+ {
+ /* special case ... */
+ if (leading_zero && bc_is_zero (num))
+ (*out_char) ('0');
+
+ /* The number is some other base. */
+ digits = NULL;
+ bc_init_num (&int_part);
+ bc_divide (num, _one_, &int_part, 0);
+ bc_init_num (&frac_part);
+ bc_init_num (&cur_dig);
+ bc_init_num (&base);
+ bc_sub (num, int_part, &frac_part, 0);
+ /* Make the INT_PART and FRAC_PART positive. */
+ int_part->n_sign = PLUS;
+ frac_part->n_sign = PLUS;
+ bc_int2num (&base, o_base);
+ bc_init_num (&max_o_digit);
+ bc_int2num (&max_o_digit, o_base-1);
+
+
+ /* Get the digits of the integer part and push them on a stack. */
+ while (!bc_is_zero (int_part))
+ {
+ bc_modulo (int_part, base, &cur_dig, 0);
+ temp = (stk_rec *) malloc (sizeof(stk_rec));
+ if (temp == NULL) bc_out_of_memory();
+ temp->digit = bc_num2long (cur_dig);
+ temp->next = digits;
+ digits = temp;
+ bc_divide (int_part, base, &int_part, 0);
+ }
+
+ /* Print the digits on the stack. */
+ if (digits != NULL)
+ {
+ /* Output the digits. */
+ while (digits != NULL)
+ {
+ temp = digits;
+ digits = digits->next;
+ if (o_base <= 16)
+ (*out_char) (ref_str[ (int) temp->digit]);
+ else
+ bc_out_long (temp->digit, max_o_digit->n_len, 1, out_char);
+ free (temp);
+ }
+ }
+
+ /* Get and print the digits of the fraction part. */
+ if (num->n_scale > 0)
+ {
+ (*out_char) ('.');
+ pre_space = 0;
+ t_num = bc_copy_num (_one_);
+ while (t_num->n_len <= num->n_scale) {
+ bc_multiply (frac_part, base, &frac_part, num->n_scale);
+ fdigit = bc_num2long (frac_part);
+ bc_int2num (&int_part, fdigit);
+ bc_sub (frac_part, int_part, &frac_part, 0);
+ if (o_base <= 16)
+ (*out_char) (ref_str[fdigit]);
+ else {
+ bc_out_long (fdigit, max_o_digit->n_len, pre_space, out_char);
+ pre_space = 1;
+ }
+ bc_multiply (t_num, base, &t_num, 0);
+ }
+ bc_free_num (&t_num);
+ }
+
+ /* Clean up. */
+ bc_free_num (&int_part);
+ bc_free_num (&frac_part);
+ bc_free_num (&base);
+ bc_free_num (&cur_dig);
+ bc_free_num (&max_o_digit);
+ }
+}
+/* Convert a number NUM to a long. The function returns only the integer
+ part of the number. For numbers that are too large to represent as
+ a long, this function returns a zero. This can be detected by checking
+ the NUM for zero after having a zero returned. */
+
+long
+bc_num2long (num)
+ bc_num num;
+{
+ long val;
+ char *nptr;
+ int index;
+
+ /* Extract the int value, ignore the fraction. */
+ val = 0;
+ nptr = num->n_value;
+ for (index=num->n_len; (index>0) && (val<=(LONG_MAX/BASE)); index--)
+ val = val*BASE + *nptr++;
+
+ /* Check for overflow. If overflow, return zero. */
+ if (index>0) val = 0;
+ if (val < 0) val = 0;
+
+ /* Return the value. */
+ if (num->n_sign == PLUS)
+ return (val);
+ else
+ return (-val);
+}
+
+
+/* Convert an integer VAL to a bc number NUM. */
+
+void
+bc_int2num (num, val)
+ bc_num *num;
+ int val;
+{
+ char buffer[30];
+ char *bptr, *vptr;
+ int ix = 1;
+ char neg = 0;
+
+ /* Sign. */
+ if (val < 0)
+ {
+ neg = 1;
+ val = -val;
+ }
+
+ /* Get things going. */
+ bptr = buffer;
+ *bptr++ = val % BASE;
+ val = val / BASE;
+
+ /* Extract remaining digits. */
+ while (val != 0)
+ {
+ *bptr++ = val % BASE;
+ val = val / BASE;
+ ix++; /* Count the digits. */
+ }
+
+ /* Make the number. */
+ bc_free_num (num);
+ *num = bc_new_num (ix, 0);
+ if (neg) (*num)->n_sign = MINUS;
+
+ /* Assign the digits. */
+ vptr = (*num)->n_value;
+ while (ix-- > 0)
+ *vptr++ = *--bptr;
+}
+
+/* Convert a numbers to a string. Base 10 only.*/
+
+char
+*bc_num2str (num)
+ bc_num num;
+{
+ char *str, *sptr;
+ char *nptr;
+ int index, signch;
+
+ /* Allocate the string memory. */
+ signch = ( num->n_sign == PLUS ? 0 : 1 ); /* Number of sign chars. */
+ if (num->n_scale > 0)
+ str = (char *) malloc (num->n_len + num->n_scale + 2 + signch);
+ else
+ str = (char *) malloc (num->n_len + 1 + signch);
+ if (str == NULL) bc_out_of_memory();
+
+ /* The negative sign if needed. */
+ sptr = str;
+ if (signch) *sptr++ = '-';
+
+ /* Load the whole number. */
+ nptr = num->n_value;
+ for (index=num->n_len; index>0; index--)
+ *sptr++ = BCD_CHAR(*nptr++);
+
+ /* Now the fraction. */
+ if (num->n_scale > 0)
+ {
+ *sptr++ = '.';
+ for (index=0; index<num->n_scale; index++)
+ *sptr++ = BCD_CHAR(*nptr++);
+ }
+
+ /* Terminate the string and return it! */
+ *sptr = '\0';
+ return (str);
+}
+/* Convert strings to bc numbers. Base 10 only.*/
+
+void
+bc_str2num (num, str, scale)
+ bc_num *num;
+ char *str;
+ int scale;
+{
+ int digits, strscale;
+ char *ptr, *nptr;
+ char zero_int;
+
+ /* Prepare num. */
+ bc_free_num (num);
+
+ /* Check for valid number and count digits. */
+ ptr = str;
+ digits = 0;
+ strscale = 0;
+ zero_int = FALSE;
+ if ( (*ptr == '+') || (*ptr == '-')) ptr++; /* Sign */
+ while (*ptr == '0') ptr++; /* Skip leading zeros. */
+ while (isdigit((int)*ptr)) ptr++, digits++; /* digits */
+ if (*ptr == '.') ptr++; /* decimal point */
+ while (isdigit((int)*ptr)) ptr++, strscale++; /* digits */
+ if ((*ptr != '\0') || (digits+strscale == 0))
+ {
+ *num = bc_copy_num (_zero_);
+ return;
+ }
+
+ /* Adjust numbers and allocate storage and initialize fields. */
+ strscale = MIN(strscale, scale);
+ if (digits == 0)
+ {
+ zero_int = TRUE;
+ digits = 1;
+ }
+ *num = bc_new_num (digits, strscale);
+
+ /* Build the whole number. */
+ ptr = str;
+ if (*ptr == '-')
+ {
+ (*num)->n_sign = MINUS;
+ ptr++;
+ }
+ else
+ {
+ (*num)->n_sign = PLUS;
+ if (*ptr == '+') ptr++;
+ }
+ while (*ptr == '0') ptr++; /* Skip leading zeros. */
+ nptr = (*num)->n_value;
+ if (zero_int)
+ {
+ *nptr++ = 0;
+ digits = 0;
+ }
+ for (;digits > 0; digits--)
+ *nptr++ = CH_VAL(*ptr++);
+
+
+ /* Build the fractional part. */
+ if (strscale > 0)
+ {
+ ptr++; /* skip the decimal point! */
+ for (;strscale > 0; strscale--)
+ *nptr++ = CH_VAL(*ptr++);
+ }
+}
+
+/* pn prints the number NUM in base 10. */
+
+static void
+out_char (int c)
+{
+ putchar(c);
+}
+
+
+void
+pn (num)
+ bc_num num;
+{
+ bc_out_num (num, 10, out_char, 0);
+ out_char ('\n');
+}
+
+
+/* pv prints a character array as if it was a string of bcd digits. */
+void
+pv (name, num, len)
+ char *name;
+ unsigned char *num;
+ int len;
+{
+ int i;
+ printf ("%s=", name);
+ for (i=0; i<len; i++) printf ("%c",BCD_CHAR(num[i]));
+ printf ("\n");
+}
+
+// vim: set et sw=2 ts=8:
diff --git a/src/number.h b/src/number.h
new file mode 100644
index 0000000..9f5e5a7
--- /dev/null
+++ b/src/number.h
@@ -0,0 +1,169 @@
+/* number.h: Arbitrary precision numbers header file. */
+/*
+ Copyright (C) 1991, 1992, 1993, 1994, 1997, 2000 Free Software Foundation, Inc.
+
+ 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; see the file COPYING. If not, write to:
+
+ The Free Software Foundation, Inc.
+ 59 Temple Place, Suite 330
+ Boston, MA 02110-1301 USA.
+
+
+ You may contact the author by:
+ e-mail: philnelson@acm.org
+ us-mail: Philip A. Nelson
+ Computer Science Department, 9062
+ Western Washington University
+ Bellingham, WA 98226-9062
+
+*************************************************************************/
+
+#ifndef _NUMBER_H_
+#define _NUMBER_H_
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#undef _PROTOTYPE
+
+#ifndef NUMBER__STDC__
+#define NUMBER__STDC__
+#endif
+
+typedef enum {PLUS, MINUS} sign;
+
+typedef struct bc_struct *bc_num;
+
+typedef struct bc_struct
+ {
+ sign n_sign;
+ int n_len; /* The number of digits before the decimal point. */
+ int n_scale; /* The number of digits after the decimal point. */
+ int n_refs; /* The number of pointers to this number. */
+ bc_num n_next; /* Linked list for available list. */
+ char *n_ptr; /* The pointer to the actual storage.
+ If NULL, n_value points to the inside of
+ another number (bc_multiply...) and should
+ not be "freed." */
+ char *n_value; /* The number. Not zero char terminated.
+ May not point to the same place as n_ptr as
+ in the case of leading zeros generated. */
+ } bc_struct;
+
+
+/* The base used in storing the numbers in n_value above.
+ Currently this MUST be 10. */
+
+#define BASE 10
+
+/* Some useful macros and constants. */
+
+#define CH_VAL(c) (c - '0')
+#define BCD_CHAR(d) (d + '0')
+
+#ifdef MIN
+#undef MIN
+#undef MAX
+#endif
+#define MAX(a,b) ((a)>(b)?(a):(b))
+#define MIN(a,b) ((a)>(b)?(b):(a))
+#define ODD(a) ((a)&1)
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+#ifndef LONG_MAX
+#define LONG_MAX 0x7ffffff
+#endif
+
+
+/* Global numbers. */
+extern bc_num _zero_;
+extern bc_num _one_;
+extern bc_num _two_;
+
+
+/* Function Prototypes */
+
+/* Define the _PROTOTYPE macro if it is needed. */
+
+#ifndef _PROTOTYPE
+#ifdef NUMBER__STDC__
+#define _PROTOTYPE(func, args) func args
+#else
+#define _PROTOTYPE(func, args) func()
+#endif
+#endif
+
+_PROTOTYPE(void bc_init_numbers, (void));
+
+_PROTOTYPE(bc_num bc_new_num, (int length, int scale));
+
+_PROTOTYPE(void bc_free_num, (bc_num *num));
+
+_PROTOTYPE(bc_num bc_copy_num, (bc_num num));
+
+_PROTOTYPE(void bc_init_num, (bc_num *num));
+
+_PROTOTYPE(void bc_str2num, (bc_num *num, char *str, int scale));
+
+_PROTOTYPE(char *bc_num2str, (bc_num num));
+
+_PROTOTYPE(void bc_int2num, (bc_num *num, int val));
+
+_PROTOTYPE(long bc_num2long, (bc_num num));
+
+_PROTOTYPE(int bc_compare, (bc_num n1, bc_num n2));
+
+_PROTOTYPE(char bc_is_zero, (bc_num num));
+
+_PROTOTYPE(char bc_is_near_zero, (bc_num num, int scale));
+
+_PROTOTYPE(char bc_is_neg, (bc_num num));
+
+_PROTOTYPE(void bc_add, (bc_num n1, bc_num n2, bc_num *result, int scale_min));
+
+_PROTOTYPE(void bc_sub, (bc_num n1, bc_num n2, bc_num *result, int scale_min));
+
+_PROTOTYPE(void bc_multiply, (bc_num n1, bc_num n2, bc_num *prod, int scale));
+
+_PROTOTYPE(int bc_divide, (bc_num n1, bc_num n2, bc_num *quot, int scale));
+
+_PROTOTYPE(int bc_modulo, (bc_num num1, bc_num num2, bc_num *result,
+ int scale));
+
+_PROTOTYPE(int bc_divmod, (bc_num num1, bc_num num2, bc_num *quot,
+ bc_num *rem, int scale));
+
+_PROTOTYPE(int bc_raisemod, (bc_num base, bc_num expo, bc_num mod,
+ bc_num *result, int scale));
+
+_PROTOTYPE(void bc_raise, (bc_num num1, bc_num num2, bc_num *result,
+ int scale));
+
+_PROTOTYPE(int bc_sqrt, (bc_num *num, int scale));
+
+_PROTOTYPE(void bc_out_num, (bc_num num, int o_base, void (* out_char)(int),
+ int leading_zero));
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+// vim: set et sw=2 ts=8:
diff --git a/src/numerictypes.cpp b/src/numerictypes.cpp
new file mode 100644
index 0000000..f2aaf7a
--- /dev/null
+++ b/src/numerictypes.cpp
@@ -0,0 +1,205 @@
+/*
+ * numerictypes.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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include "numerictypes.h"
+#include "hmath.h"
+
+#include <kdebug.h>
+#include <kglobal.h>
+#include <klocale.h>
+
+Abakus::TrigMode Abakus::m_trigMode = Abakus::Degrees;
+int Abakus::m_prec = -1;
+
+#if HAVE_MPFR
+
+namespace Abakus
+{
+
+QString convertToString(const mpfr_ptr &number)
+{
+ char *str = 0;
+ QRegExp zeroKiller ("0*$");
+ mp_exp_t exp;
+ int desiredPrecision = Abakus::m_prec;
+ QString decimalSymbol = KGlobal::locale()->decimalSymbol();
+
+ if(desiredPrecision < 0)
+ desiredPrecision = 8;
+
+ // This first call is to see approximately how many digits of precision
+ // the fractional part has.
+ str = mpfr_get_str (0, &exp, 10, desiredPrecision, number, GMP_RNDN);
+
+ // Check for ginormously small numbers.
+ if(exp < -74)
+ return "0";
+
+ if(exp < -2 || exp > desiredPrecision)
+ {
+ // Use exponential notation.
+ QString numbers (str);
+ mpfr_free_str(str);
+
+ QString sign, l, r;
+ if(numbers[0] == '-')
+ {
+ sign = "-";
+ l = numbers[1];
+ r = numbers.right(numbers.length() - 2);
+ }
+ else
+ {
+ l = numbers[0];
+ r = numbers.right(numbers.length() - 1);
+ }
+
+ // Remove trailing zeroes.
+ if(Abakus::m_prec < 0)
+ r.replace(zeroKiller, "");
+
+ // But don't display numbers like 2.e10 either.
+ if(r.isEmpty())
+ r = "0";
+
+ r.append(QString("e%1").arg(exp - 1));
+
+ return sign + l + decimalSymbol + r;
+ }
+ else
+ {
+ mpfr_free_str(str);
+
+ // This call is to adjust the result so that the fractional part has at
+ // most m_prec digits of precision.
+ str = mpfr_get_str (0, &exp, 10, exp + desiredPrecision, number, GMP_RNDN);
+ }
+
+ QString result = str;
+ mpfr_free_str(str);
+ str = 0;
+
+ int position = exp;
+ QString l, r, sign;
+
+ if(position < 0) { // Number < 0.1
+ l.fill('0', -position);
+
+ if(result[0] == '-') {
+ sign = "-";
+ r = result.right(result.length() - 1);
+ }
+ else
+ r = result;
+
+ r = l + r;
+ l = '0';
+ }
+ else { // Number >= 0.1
+ if(result[0] == '-') {
+ l = result.mid(1, position);
+ sign = "-";
+ position++;
+ }
+ else
+ l = result.left(position);
+
+ r = result.right(result.length() - position);
+ }
+
+ // Remove trailing zeroes.
+ r.replace(zeroKiller, "");
+
+ // Don't display numbers of the form .23
+ if(l.isEmpty())
+ l = "0";
+
+ // If we have an integer don't display the decimal part.
+ if(r.isEmpty())
+ return sign + l;
+
+ return sign + l + decimalSymbol + r;
+}
+
+} // namespace Abakus
+
+Abakus::number_t::value_type setupPi()
+{
+ static mpfr_t pi;
+
+ mpfr_init2 (pi, 250);
+ mpfr_const_pi (pi, GMP_RNDN);
+
+ return pi;
+}
+
+Abakus::number_t::value_type setupExponential()
+{
+ static mpfr_t exponential;
+ mpfr_t one;
+
+ mpfr_init2 (exponential, 250);
+ mpfr_init_set_ui (one, 1, GMP_RNDN);
+ mpfr_exp (exponential, one, GMP_RNDN);
+ mpfr_clear (one);
+
+ return exponential;
+}
+
+const Abakus::number_t::value_type Abakus::number_t::PI = setupPi();
+const Abakus::number_t::value_type Abakus::number_t::E = setupExponential();
+
+#else
+
+// Converts hmath number to a string.
+
+namespace Abakus
+{
+
+QString convertToString(const HNumber &num)
+{
+ QString str = HMath::formatGenString(num, m_prec);
+ QString decimalSymbol = KGlobal::locale()->decimalSymbol();
+ str.replace('.', decimalSymbol);
+
+ QStringList parts = QStringList::split("e", str);
+ QRegExp zeroKiller("(" + QRegExp::escape(decimalSymbol) +
+ "\\d*[1-9])0*$"); // Remove trailing zeroes.
+ QRegExp zeroKiller2("(" + QRegExp::escape(decimalSymbol) + ")0*$");
+
+ str = parts[0];
+ str.replace(zeroKiller, "\\1");
+ str.replace(zeroKiller2, "\\1");
+ if(str.endsWith(decimalSymbol))
+ str.truncate(str.length() - 1); // Remove trailing period.
+
+ if(parts.count() > 1 && parts[1] != "0")
+ str += QString("e%1").arg(parts[1]);
+
+ return str;
+}
+
+} // namespace Abakus.
+
+const Abakus::number_t::value_type Abakus::number_t::PI = HMath::pi();
+const Abakus::number_t::value_type Abakus::number_t::E = HMath::exp(1);
+
+#endif /* HAVE_MPFR */
+
+// vim: set et ts=8 sw=4:
diff --git a/src/numerictypes.h b/src/numerictypes.h
new file mode 100644
index 0000000..7a82d06
--- /dev/null
+++ b/src/numerictypes.h
@@ -0,0 +1,693 @@
+#ifndef ABAKUS_NUMERICTYPES_H
+#define ABAKUS_NUMERICTYPES_H
+/*
+ * numerictypes.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include <sstream>
+#include <string>
+
+#include <qstring.h>
+#include <qstringlist.h>
+#include <qregexp.h>
+
+#include "hmath.h"
+#include "config.h"
+
+#if HAVE_MPFR
+#include <mpfr.h>
+#endif
+
+namespace Abakus
+{
+
+/* What trigonometric mode we're in. */
+typedef enum { Degrees, Radians } TrigMode;
+
+/* Shared application-wide */
+extern TrigMode m_trigMode;
+
+/* Precision to display at. */
+extern int m_prec;
+
+/**
+ * Representation of a number type. Includes the basic operators, along with
+ * built-in functions such as abs() and mod().
+ *
+ * You need to actually define it using template specializations though. You
+ * can add functions in a specialization, it may be worth it to have the
+ * functions declared here as well so that you get a compiler error if you
+ * forget to implement it.
+ *
+ * Note that since we're using a specialization, and then typedef'ing the
+ * new specialized class to number_t, that means we only support one type of
+ * number at a time, and the choice is made at compile-time.
+ */
+template <typename T>
+class number
+{
+public:
+ /// Default ctor and set-and-assign ctor wrapped in one.
+ number(const T& t = T());
+
+ /// Copy constructor.
+ number(const number &other);
+
+ /// Create number from textual representation, useful for ginormously
+ /// precise numbers.
+ number(const char *str);
+
+ /// Convienience constructor to create a number from an integer.
+ explicit number(int i);
+
+ /// Assignment operator. Be sure to check for &other == this if necessary!
+ number<T> &operator =(const number<T> &other);
+
+ // You need to implement the suite of comparison operators as well, along
+ // with the negation operator. Sorry.
+
+ bool operator!=(const number<T> &other) const;
+ bool operator==(const number<T> &other) const;
+ bool operator<(const number<T> &other) const;
+ bool operator>(const number<T> &other) const;
+ bool operator<=(const number<T> &other) const;
+ bool operator>=(const number<T> &other) const;
+
+ number<T> operator -() const;
+
+ // These functions must be implemented by all specializations to be used.
+ // Note that when implementing these functions, the implicit value is the
+ // value that this object is wrapping. E.g. you'd call the function on
+ // a number object, kind of like 3.sin() if you were using Ruby.
+
+ // Trigonometric, must accept values in degrees.
+ number<T> sin() const;
+ number<T> cos() const;
+ number<T> tan() const;
+
+ // Inverse trigonometric, must return result in Degrees if necessary.
+ number<T> asin() const;
+ number<T> acos() const;
+ number<T> atan() const;
+
+ // Hyperbolic trigonometric (doesn't use Degrees).
+ number<T> sinh() const;
+ number<T> cosh() const;
+ number<T> tanh() const;
+
+ // Inverse hyperbolic trigonometric (doesn't use degrees).
+ number<T> asinh() const;
+ number<T> acosh() const;
+ number<T> atanh() const;
+
+ /// @return Number rounded to closest integer less than or equal to value.
+ number<T> floor() const;
+
+ /// @return Number rounded to closest integer greater than or equal to value.
+ number<T> ceil() const;
+
+ /// @return Number with only integer component of result.
+ number<T> integer() const;
+
+ /// @return Number with only fractional component of result.
+ number<T> frac() const;
+
+ /**
+ * @return Number rounded to nearest integer. What to do in 'strange'
+ * situations is specialization-dependant, I don't really care enough to
+ * mandate one or the other.
+ */
+ number<T> round() const;
+
+ /// @return Absolute value of number.
+ number<T> abs() const;
+
+ /// @return Square root of number.
+ number<T> sqrt() const;
+
+ /// @return Natural-base logarithm of value.
+ number<T> ln() const;
+
+ /// @return base-10 logarithm of value.
+ number<T> log() const;
+
+ /// @return Natural base raised to the power given by our value.
+ number<T> exp() const;
+
+ /// @return Our value raised to the \p exponent power. Would be nice if
+ /// it supported even exponents on negative numbers correctly.
+ number<T> pow(const number<T> &exponent);
+
+ /// @return value rounded to double precision.
+ double asDouble() const;
+
+ /// @return Textual representation of the number, adjusted to the user's
+ /// current precision.
+ QString toString() const;
+
+ /// @return Our value.
+ T value() const;
+};
+
+// You should also remember to overload the math operators for your
+// specialization. These generic ones should work for templates wrapping a
+// type that C++ already has operators for.
+
+template<typename T>
+inline number<T> operator+(const number<T> &l, const number<T> &r)
+{
+ return number<T>(l.value() + r.value());
+}
+
+template<typename T>
+inline number<T> operator-(const number<T> &l, const number<T> &r)
+{
+ return number<T>(l.value() - r.value());
+}
+
+template<typename T>
+inline number<T> operator*(const number<T> &l, const number<T> &r)
+{
+ return number<T>(l.value() * r.value());
+}
+
+template<typename T>
+inline number<T> operator/(const number<T> &l, const number<T> &r)
+{
+ return number<T>(l.value() / r.value());
+}
+
+#if HAVE_MPFR
+
+/**
+ * Utility function to convert a MPFR number to a string. This is declared
+ * this way so that when it changes we don't have to recompile all of Abakus.
+ *
+ * This function obeys the precision settings of the user. This means that if
+ * you change the precision between function calls, you may get different
+ * results, even on the same number!
+ *
+ * But, don't use this directly, you should be using
+ * number<mpfr_ptr>::toString() instead!
+ *
+ * @param number MPFR number to convert to string.
+ * @return The number converted to a string, in US Decimal format at this time.
+ * @see number<>::toString()
+ */
+QString convertToString(const mpfr_ptr &number);
+
+/**
+ * This is a specialization of the number<> template for the MPFR numeric type.
+ * It uses a weird hack in that it is declared as specializing mpfr_ptr instead
+ * of mpfr_t like is used everywhere in MPFR's public API.
+ *
+ * This is because mpfr_t does not seem to play well with C++ templates (it
+ * is implemented internally as a 1-length array to get pointer semantics
+ * while also allocating memory.
+ *
+ * What this means is that should you ever have to deal with allocating
+ * memory, you need to use allocate space for it (mpfr_ptr is a pointer to
+ * __mpfr_struct).
+ *
+ * I don't like using the internal API this way, but I have little choice.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+template<>
+class number<mpfr_ptr>
+{
+public:
+ typedef mpfr_ptr value_type;
+
+ static const mp_rnd_t RoundDirection = GMP_RNDN;
+
+ number(const value_type& t)
+ {
+ m_t = (mpfr_ptr) new __mpfr_struct;
+ mpfr_init_set(m_t, t, RoundDirection);
+ }
+
+ number(const number<value_type> &other)
+ {
+ m_t = (mpfr_ptr) new __mpfr_struct;
+ mpfr_init_set(m_t, other.m_t, RoundDirection);
+ }
+
+ number(const char *str)
+ {
+ m_t = (mpfr_ptr) new __mpfr_struct;
+ mpfr_init_set_str (m_t, str, 10, RoundDirection);
+ }
+
+ explicit number(int i)
+ {
+ m_t = (mpfr_ptr) new __mpfr_struct;
+ mpfr_init_set_si(m_t, (signed long int) i, RoundDirection);
+ }
+
+ /// Construct a number with a value of NaN.
+ number()
+ {
+ m_t = (mpfr_ptr) new __mpfr_struct;
+ mpfr_init(m_t);
+ }
+
+ ~number()
+ {
+ mpfr_clear(m_t);
+ delete (__mpfr_struct *) m_t;
+ }
+
+ number<value_type> &operator=(const number<value_type> &other)
+ {
+ if(&other == this)
+ return *this;
+
+ mpfr_clear (m_t);
+ mpfr_init_set (m_t, other.m_t, RoundDirection);
+
+ return *this;
+ }
+
+ bool operator!=(const number<value_type> &other) const
+ {
+ return mpfr_equal_p(m_t, other.m_t) == 0;
+ }
+
+ bool operator==(const number<value_type> &other) const
+ {
+ return mpfr_equal_p(m_t, other.m_t) != 0;
+ }
+
+ bool operator<(const number<value_type> &other) const
+ {
+ return mpfr_less_p(m_t, other.m_t) != 0;
+ }
+
+ bool operator>(const number<value_type> &other) const
+ {
+ return mpfr_greater_p(m_t, other.m_t) != 0;
+ }
+
+ bool operator<=(const number<value_type> &other) const
+ {
+ return mpfr_lessequal_p(m_t, other.m_t) != 0;
+ }
+
+ bool operator>=(const number<value_type> &other) const
+ {
+ return mpfr_greaterequal_p(m_t, other.m_t) != 0;
+ }
+
+ number<value_type> operator -() const
+ {
+ number<value_type> result(m_t);
+ mpfr_neg(result.m_t, result.m_t, RoundDirection);
+
+ return result;
+ }
+
+ // internal
+ number<value_type> asRadians() const
+ {
+ if(m_trigMode == Degrees)
+ {
+ number<value_type> result(m_t);
+ mpfr_t pi;
+
+ mpfr_init (pi);
+ mpfr_const_pi (pi, RoundDirection);
+ mpfr_mul (result.m_t, result.m_t, pi, RoundDirection);
+ mpfr_div_ui (result.m_t, result.m_t, 180, RoundDirection);
+
+ mpfr_clear (pi);
+
+ return result;
+ }
+ else
+ return m_t;
+ }
+
+ // internal
+ number<value_type> toTrig() const
+ {
+ // Assumes num is in radians.
+ if(m_trigMode == Degrees)
+ {
+ number<value_type> result(m_t);
+ mpfr_t pi;
+
+ mpfr_init (pi);
+ mpfr_const_pi (pi, RoundDirection);
+ mpfr_mul_ui (result.m_t, result.m_t, 180, RoundDirection);
+ mpfr_div (result.m_t, result.m_t, pi, RoundDirection);
+
+ mpfr_clear (pi);
+
+ return result;
+ }
+ else
+ return m_t;
+ }
+
+/* There is a lot of boilerplate ahead, so define a macro to declare and
+ * define some functions for us to forward the call to MPFR.
+ */
+#define DECLARE_IMPL_BASE(name, func, in, out) number<value_type> name() const \
+{ \
+ number<value_type> result = in; \
+ mpfr_##func (result.m_t, result.m_t, RoundDirection); \
+ \
+ return out; \
+}
+
+// Normal function, uses 2 rather than 3 params
+#define DECLARE_NAMED_IMPL2(name, func) number<value_type> name() const \
+{ \
+ number<value_type> result = m_t; \
+ mpfr_##func (result.m_t, result.m_t); \
+ \
+ return result; \
+}
+
+// Normal function, but MPFL uses a different name than abakus.
+#define DECLARE_NAMED_IMPL(name, func) DECLARE_IMPL_BASE(name, func, m_t, result)
+
+// Normal function, just routes call to MPFR.
+#define DECLARE_IMPL(name) DECLARE_NAMED_IMPL(name, name)
+
+// Trig function, degrees in
+#define DECLARE_TRIG_IN_IMPL(name) DECLARE_IMPL_BASE(name, name, asRadians(), result)
+
+// Trig function, degrees out
+#define DECLARE_TRIG_OUT_IMPL(name) DECLARE_IMPL_BASE(name, name, m_t, result.toTrig())
+
+// Now declare our functions.
+ DECLARE_TRIG_IN_IMPL(sin)
+ DECLARE_TRIG_IN_IMPL(cos)
+ DECLARE_TRIG_IN_IMPL(tan)
+
+ DECLARE_IMPL(sinh)
+ DECLARE_IMPL(cosh)
+ DECLARE_IMPL(tanh)
+
+ DECLARE_TRIG_OUT_IMPL(asin)
+ DECLARE_TRIG_OUT_IMPL(acos)
+ DECLARE_TRIG_OUT_IMPL(atan)
+
+ DECLARE_IMPL(asinh)
+ DECLARE_IMPL(acosh)
+ DECLARE_IMPL(atanh)
+
+ DECLARE_NAMED_IMPL2(floor, floor)
+ DECLARE_NAMED_IMPL2(ceil, ceil)
+ DECLARE_NAMED_IMPL(integer, rint)
+ DECLARE_IMPL(frac)
+ DECLARE_NAMED_IMPL2(round, round)
+
+ DECLARE_IMPL(abs)
+ DECLARE_IMPL(sqrt)
+ DECLARE_NAMED_IMPL(ln, log)
+ DECLARE_NAMED_IMPL(log, log10)
+ DECLARE_IMPL(exp)
+
+ // Can't use macro for this one, it's sorta weird.
+ number<value_type> pow(const number<value_type> &exponent)
+ {
+ number<value_type> result = m_t;
+
+ mpfr_pow(result.m_t, result.m_t, exponent.m_t, RoundDirection);
+ return result;
+ }
+
+ double asDouble() const
+ {
+ return mpfr_get_d(m_t, RoundDirection);
+ }
+
+ // Note that this can be used dangerously, be careful.
+ value_type value() const { return m_t; }
+
+ QString toString() const
+ {
+ // Move this to .cpp to avoid recompiling as I fix it.
+ return convertToString(m_t);
+ }
+
+ static number<value_type> nan()
+ {
+ // Doesn't apply, but the default value when initialized happens
+ // to be nan.
+ return number<value_type>();
+ }
+
+ static const value_type PI;
+ static const value_type E;
+
+private:
+ mpfr_ptr m_t;
+};
+
+// Specializations of math operators for mpfr.
+
+template<>
+inline number<mpfr_ptr> operator+(const number<mpfr_ptr> &l, const number<mpfr_ptr> &r)
+{
+ number<mpfr_ptr> result;
+ mpfr_add(result.value(), l.value(), r.value(), GMP_RNDN);
+
+ return result;
+}
+
+template<>
+inline number<mpfr_ptr> operator-(const number<mpfr_ptr> &l, const number<mpfr_ptr> &r)
+{
+ number<mpfr_ptr> result;
+ mpfr_sub(result.value(), l.value(), r.value(), GMP_RNDN);
+
+ return result;
+}
+
+template<>
+inline number<mpfr_ptr> operator*(const number<mpfr_ptr> &l, const number<mpfr_ptr> &r)
+{
+ number<mpfr_ptr> result;
+ mpfr_mul(result.value(), l.value(), r.value(), GMP_RNDN);
+
+ return result;
+}
+
+template<>
+inline number<mpfr_ptr> operator/(const number<mpfr_ptr> &l, const number<mpfr_ptr> &r)
+{
+ number<mpfr_ptr> result;
+ mpfr_div(result.value(), l.value(), r.value(), GMP_RNDN);
+
+ return result;
+}
+
+ // Abakus namespace continues.
+ typedef number<mpfr_ptr> number_t;
+
+#else
+
+// Defined in numerictypes.cpp for ease of reimplementation.
+QString convertToString(const HNumber &num);
+
+/**
+ * Specialization for internal HMath library, used if MPFR isn't usable.
+ *
+ * @author Michael Pyne <michael.pyne@kdemail.net>
+ */
+template<>
+class number<HNumber>
+{
+public:
+ typedef HNumber value_type;
+
+ number(const HNumber& t = HNumber()) : m_t(t)
+ {
+ }
+ explicit number(int i) : m_t(i) { }
+ number(const number<HNumber> &other) : m_t(other.m_t) { }
+
+ number(const char *s) : m_t(s) { }
+
+ bool operator!=(const number<HNumber> &other) const
+ {
+ return m_t != other.m_t;
+ }
+
+ bool operator==(const number<HNumber> &other) const
+ {
+ return m_t == other.m_t;
+ }
+
+ bool operator<(const number<HNumber> &other) const
+ {
+ return m_t < other.m_t;
+ }
+
+ bool operator>(const number<HNumber> &other) const
+ {
+ return m_t > other.m_t;
+ }
+
+ bool operator<=(const number<HNumber> &other) const
+ {
+ return m_t <= other.m_t;
+ }
+
+ bool operator>=(const number<HNumber> &other) const
+ {
+ return m_t >= other.m_t;
+ }
+
+ number<HNumber> &operator=(const number<HNumber> &other)
+ {
+ m_t = other.m_t;
+ return *this;
+ }
+
+ HNumber asRadians() const
+ {
+ if(m_trigMode == Degrees)
+ return m_t * PI / HNumber("180.0");
+ else
+ return m_t;
+ }
+
+ HNumber toTrig(const HNumber &num) const
+ {
+ // Assumes num is in radians.
+ if(m_trigMode == Degrees)
+ return num * HNumber("180.0") / PI;
+ else
+ return num;
+ }
+
+ number<HNumber> sin() const
+ {
+ return HMath::sin(asRadians());
+ }
+
+ number<HNumber> cos() const
+ {
+ return HMath::cos(asRadians());
+ }
+
+ number<HNumber> tan() const
+ {
+ return HMath::tan(asRadians());
+ }
+
+ number<HNumber> asin() const
+ {
+ return toTrig(HMath::asin(m_t));
+ }
+
+ number<HNumber> acos() const
+ {
+ return toTrig(HMath::acos(m_t));
+ }
+
+ number<HNumber> atan() const
+ {
+ return toTrig(HMath::atan(m_t));
+ }
+
+ number<HNumber> floor() const
+ {
+ if(HMath::frac(m_t) == HNumber("0.0"))
+ return integer();
+ if(HMath::integer(m_t) < HNumber("0.0"))
+ return HMath::integer(m_t) - 1;
+ return integer();
+ }
+
+ number<HNumber> ceil() const
+ {
+ return floor().value() + HNumber(1);
+ }
+
+/* There is a lot of boilerplate ahead, so define a macro to declare and
+ * define some functions for us to forward the call to HMath.
+ */
+#define DECLARE_IMPL(name) number<value_type> name() const \
+{ return HMath::name(m_t); }
+
+ DECLARE_IMPL(frac)
+ DECLARE_IMPL(integer)
+ DECLARE_IMPL(round)
+
+ DECLARE_IMPL(abs)
+
+ DECLARE_IMPL(sqrt)
+
+ DECLARE_IMPL(ln)
+ DECLARE_IMPL(log)
+ DECLARE_IMPL(exp)
+
+ DECLARE_IMPL(sinh)
+ DECLARE_IMPL(cosh)
+ DECLARE_IMPL(tanh)
+
+ DECLARE_IMPL(asinh)
+ DECLARE_IMPL(acosh)
+ DECLARE_IMPL(atanh)
+
+ HNumber value() const { return m_t; }
+
+ double asDouble() const { return toString().toDouble(); }
+
+ number<HNumber> operator-() const { return HMath::negate(m_t); }
+
+ // TODO: I believe this doesn't work for negative numbers with even
+ // exponents. Which breaks simple stuff like (-2)^2. :(
+ number<HNumber> pow(const number<HNumber> &exponent)
+ {
+ return HMath::raise(m_t, exponent.m_t);
+ }
+
+ QString toString() const
+ {
+ return convertToString(m_t);
+ }
+
+ static number<HNumber> nan()
+ {
+ return HNumber::nan();
+ }
+
+ static const HNumber PI;
+ static const HNumber E;
+
+private:
+ HNumber m_t;
+};
+
+ // Abakus namespace continues.
+ typedef number<HNumber> number_t;
+
+#endif /* HAVE_MPFR */
+
+}; // namespace Abakus
+
+#endif /* ABAKUS_NUMERICTYPES_H */
+
+// vim: set et ts=8 sw=4:
diff --git a/src/parser.yy b/src/parser.yy
new file mode 100644
index 0000000..5a93621
--- /dev/null
+++ b/src/parser.yy
@@ -0,0 +1,386 @@
+/*
+ * parser.yy - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+%{
+
+/* Add necessary includes here. */
+#include <kdebug.h>
+#include <klocale.h>
+#include <kglobal.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <math.h>
+
+#include "result.h"
+#include "node.h"
+#include "function.h"
+#include "valuemanager.h"
+
+extern char *yytext;
+
+extern int gCheckIdents;
+
+int yylex(void);
+int yyerror(const char *);
+
+%}
+
+%union {
+ Node *node;
+ NumericValue *value;
+ UnaryFunction *fn;
+ Identifier *ident;
+}
+
+%token <value> NUM
+%type <node> EXP FACTOR TERM S EXPONENT NUMBER VALUE FINAL
+%type <fn> FUNC
+%token <fn> FN
+%token <ident> ID
+%type <ident> IDENT ASSIGN
+%token POWER "**"
+%token SET "set"
+%token REMOVE "remove"
+%token DERIV "deriv"
+
+%%
+
+/**
+ * Parser design:
+ *
+ * This is pretty standard stuff for the calculator part (read in tokens from
+ * the lexer, and form a syntax tree using the Node* objects). The unusual
+ * part is that due to the design of bison, we don't actually return a value
+ * normally to the calling function.
+ *
+ * Instead, we make use of the static Result::setLastResult() call in order
+ * to notify the calling function of the result of the parse. There are
+ * different statuses you can set, including Error (with a message), Null
+ * (which indicates that some action happened that doesn't generate a result),
+ * and Value (with a Node* that holds the result).
+ *
+ * If you are done parsing before reaching the FINAL token, you can call:
+ * YYACCEPT: Done, parsed successfully.
+ * YYERROR : Done, there was an error.
+ *
+ * Note that if you let the parse bubble back up to FINAL, then the result
+ * will always be a Value.
+ */
+
+FINAL: { gCheckIdents = 1; } S {
+ Result::setLastResult(NodePtr($2));
+ $$ = 0;
+}
+
+S: EXP { $$ = $1; }
+
+// Rudimentary error handling
+S: error '=' {
+ Result::setLastResult(i18n("This is an invalid assignment."));
+
+ YYABORT;
+}
+
+// Can't assign to a function.
+S: FUNC '=' {
+ QString s(i18n("You can't assign to function %1").arg($1->name()));
+ Result::setLastResult(s);
+
+ YYABORT;
+}
+
+// This is a function prototype. abakus currently only supports one-argument
+// functions.
+ASSIGN: '(' { --gCheckIdents; } IDENT ')' '=' {
+ $$ = $3;
+}
+
+// Blocking a variable with the name deriv is a slight feature regression
+// since normally functions and variables with the same name can coexist, but
+// I don't want to duplicate code all over the place.
+S: SET DERIV {
+ QString s(i18n("Function %1 is built-in and cannot be overridden.").arg("deriv"));
+ Result::setLastResult(s);
+
+ YYABORT;
+}
+
+S: DERIV '=' {
+ QString s(i18n("Function %1 is built-in and cannot be overridden.").arg("deriv"));
+ Result::setLastResult(s);
+
+ YYABORT;
+}
+
+S: SET FUNC ASSIGN EXP {
+ ++gCheckIdents;
+
+ // We're trying to reassign an already defined function, make sure it's
+ // not a built-in.
+ QString funcName = $2->name();
+ QString ident = $3->name();
+ FunctionManager *manager = FunctionManager::instance();
+
+ if(manager->isFunction(funcName) && !manager->isFunctionUserDefined(funcName)) {
+ QString s(i18n("Function %1 is built-in and cannot be overridden.").arg(funcName));
+ Result::setLastResult(s);
+
+ YYABORT;
+ }
+
+ if(manager->isFunction(funcName))
+ manager->removeFunction(funcName);
+
+ BaseFunction *newFn = new UserDefinedFunction(funcName, $4);
+ if(!manager->addFunction(newFn, ident)) {
+ QString s(i18n("Unable to define function %1 because it is recursive.").arg(funcName));
+ Result::setLastResult(s);
+
+ YYABORT;
+ }
+
+ Result::setLastResult(Result::Null);
+ YYACCEPT;
+}
+
+// IDENT is the same as FUNC, except that the lexer has determined that IDENT
+// is not already a FUNC.
+S: SET IDENT ASSIGN EXP {
+ ++gCheckIdents;
+
+ QString funcName = $2->name();
+ QString ident = $3->name();
+
+ // No need to check if the function is already defined, because the
+ // lexer checked for us before returning the IDENT token.
+ BaseFunction *newFn = new UserDefinedFunction(funcName, $4);
+ FunctionManager::instance()->addFunction(newFn, ident);
+
+ Result::setLastResult(Result::Null);
+ YYACCEPT;
+}
+
+// Remove a defined function.
+S: REMOVE FUNC '(' ')' {
+ FunctionManager::instance()->removeFunction($2->name());
+
+ Result::setLastResult(Result::Null);
+ YYACCEPT;
+}
+
+// Can't remove an ident using remove-func syntax.
+S: REMOVE IDENT '(' ')' {
+ // This is an error
+ Result::setLastResult(Result(i18n("Function %1 is not defined.").arg($2->name())));
+ YYABORT;
+}
+
+// This happens when the user tries to remove a function that's not defined.
+S: REMOVE IDENT '(' IDENT ')' {
+ // This is an error
+ Result::setLastResult(Result(i18n("Function %1 is not defined.").arg($2->name())));
+ YYABORT;
+}
+
+S: REMOVE IDENT {
+ ValueManager *manager = ValueManager::instance();
+
+ if(manager->isValueSet($2->name()) && !manager->isValueReadOnly($2->name())) {
+ manager->removeValue($2->name());
+
+ Result::setLastResult(Result::Null);
+ YYACCEPT;
+ }
+ else {
+ QString s;
+ if(manager->isValueSet($2->name()))
+ s = i18n("Can't remove predefined variable %1.").arg($2->name());
+ else
+ s = i18n("Can't remove undefined variable %1.").arg($2->name());
+
+ Result::setLastResult(s);
+
+ YYABORT;
+ }
+}
+
+S: SET IDENT '=' EXP {
+ ValueManager *vm = ValueManager::instance();
+
+ if(vm->isValueReadOnly($2->name())) {
+ if($2->name() == "pi" && $4->value() == Abakus::number_t("3.0"))
+ Result::setLastResult(i18n("This isn't Indiana, you can't just change pi"));
+ else
+ Result::setLastResult(i18n("%1 is a constant").arg($2->name()));
+
+ YYABORT;
+ }
+
+ ValueManager::instance()->setValue($2->name(), $4->value());
+
+ Result::setLastResult(Result::Null);
+ YYACCEPT;
+}
+
+// Set a variable.
+S: IDENT '=' EXP {
+ ValueManager *vm = ValueManager::instance();
+
+ if(vm->isValueReadOnly($1->name())) {
+ if($1->name() == "pi" && $3->value() == Abakus::number_t("3.0"))
+ Result::setLastResult(i18n("This isn't Indiana, you can't just change pi"));
+ else
+ Result::setLastResult(i18n("%1 is a constant").arg($1->name()));
+
+ YYABORT;
+ }
+
+ ValueManager::instance()->setValue($1->name(), $3->value());
+
+ Result::setLastResult(Result::Null);
+ YYACCEPT;
+}
+
+S: NUMBER '=' {
+ Result::setLastResult(i18n("Can't assign to %1").arg($1->value().toString()));
+ YYABORT;
+}
+
+// Can't call this as a function.
+TERM: IDENT '(' {
+ Result::setLastResult(i18n("%1 isn't a function (or operator expected)").arg($1->name()));
+ YYABORT;
+}
+
+// Can't do this either.
+TERM: IDENT IDENT {
+ Result::setLastResult(i18n("Missing operator"));
+ YYABORT;
+}
+
+TERM: IDENT NUMBER {
+ Result::setLastResult(i18n("Missing operator"));
+ YYABORT;
+}
+
+TERM: NUMBER NUMBER {
+ Result::setLastResult(i18n("Missing operator"));
+ YYABORT;
+}
+
+S: error {
+ Result::setLastResult(i18n("Sorry, I can't figure it out."));
+ YYABORT;
+}
+
+/**
+ * Here be the standard calculator-parsing part. Nothing here should be too
+ * fancy.
+ */
+EXP: EXP '+' FACTOR { $$ = new BinaryOperator(BinaryOperator::Addition, $1, $3); }
+EXP: EXP '-' FACTOR { $$ = new BinaryOperator(BinaryOperator::Subtraction, $1, $3); }
+EXP: FACTOR { $$ = $1; }
+
+FACTOR: FACTOR '*' EXPONENT { $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $3); }
+FACTOR: FACTOR '/' EXPONENT { $$ = new BinaryOperator(BinaryOperator::Division, $1, $3); }
+FACTOR: EXPONENT { $$ = $1; }
+
+EXPONENT: TERM POWER EXPONENT { $$ = new BinaryOperator(BinaryOperator::Exponentiation, $1, $3); }
+EXPONENT: TERM { $$ = $1; }
+
+TERM: '+' VALUE { $$ = $2; }
+TERM: '-' VALUE { $$ = new UnaryOperator(UnaryOperator::Negation, $2); }
+TERM: '(' EXP ')' { $$ = $2; }
+TERM: '-' '(' EXP ')' { $$ = new UnaryOperator(UnaryOperator::Negation, $3); }
+
+TERM: VALUE { $$ = $1; }
+
+VALUE: NUMBER { $$ = $1; }
+
+NUMBER: NUM {
+ KLocale *locale = KGlobal::locale();
+ QChar decimal = locale->decimalSymbol()[0];
+
+ // Replace current decimal separator with US Decimal separator to be
+ // evil.
+ unsigned len = strlen(yytext);
+ for(unsigned i = 0; i < len; ++i)
+ if(yytext[i] == decimal)
+ yytext[i] = '.';
+
+ Abakus::number_t value(yytext);
+
+ $$ = new NumericValue(value);
+}
+
+TERM: DERIV { --gCheckIdents; } '(' EXP ',' { ++gCheckIdents; } EXP ')' {
+ $$ = new DerivativeFunction($4, $7);
+}
+
+TERM: FUNC TERM {
+ $1->setOperand($2);
+ $$ = $1;
+}
+
+/* Handle implicit multiplication */
+TERM: NUMBER FUNC TERM {
+ $2->setOperand($3);
+ $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $2);
+}
+
+TERM: NUMBER '(' EXP ')' {
+ $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $3);
+}
+
+TERM: NUMBER IDENT {
+ if(gCheckIdents > 0 && !ValueManager::instance()->isValueSet($2->name())) {
+ Result::setLastResult(i18n("Unknown variable %1").arg($2->name()));
+ YYABORT;
+ }
+
+ $$ = new BinaryOperator(BinaryOperator::Multiplication, $1, $2);
+}
+
+VALUE: IDENT {
+ if(gCheckIdents <= 0 || ValueManager::instance()->isValueSet($1->name()))
+ $$ = $1;
+ else {
+ Result::setLastResult(i18n("Unknown variable %1").arg($1->name()));
+ YYABORT;
+ }
+}
+
+IDENT: ID {
+ $$ = new Identifier(yytext);
+}
+
+FUNC: FN {
+ /* No check necessary, the lexer has already checked for us. */
+ $$ = new BuiltinFunction(yytext, 0);
+}
+
+%%
+
+int gCheckIdents = 0;
+
+int yyerror(const char *)
+{
+ return 0;
+}
diff --git a/src/result.cpp b/src/result.cpp
new file mode 100644
index 0000000..4aa06ca
--- /dev/null
+++ b/src/result.cpp
@@ -0,0 +1,33 @@
+/*
+ * result.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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+#include "result.h"
+
+Result *Result::m_lastResult = new Result;
+
+Result::Result(const QString &message) : m_type(Error), m_message(message)
+{
+}
+
+Result::Result(NodePtr node) : m_node(node), m_type(Value)
+{
+}
+
+Result::Result(Type type) : m_type(type)
+{
+}
diff --git a/src/result.h b/src/result.h
new file mode 100644
index 0000000..682324c
--- /dev/null
+++ b/src/result.h
@@ -0,0 +1,77 @@
+#ifndef ABAKUS_RESULT_H
+#define ABAKUS_RESULT_H
+/*
+ * result.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include <qstring.h>
+
+#include "node.h"
+#include "sharedptr.h"
+
+/**
+ * A conceptual result from an expression parse. Used to determine if the
+ * parse succeeded or failed. If it succeeded it will have a node value you
+ * can query as the answer.
+ */
+class Result
+{
+public:
+ typedef enum { Error, Null, Value } Type;
+
+ /**
+ * Default constructor, which constructs a "failed" Result.
+ */
+ Result(const QString &message = "");
+
+ /**
+ * Node constructor, which constructs a "succeeded" result.
+ */
+ Result(NodePtr node);
+
+ /**
+ * Constructor, constructs a "null" result. This means that the
+ * operation was successful, but did not result in a normal value.
+ */
+ Result(Type type);
+
+ bool failed() const { return m_type == Error; }
+
+ Type type() const { return m_type; }
+
+ QString message() const { return m_message; }
+
+ const NodePtr result() const { return m_node; }
+ NodePtr result() { return m_node; }
+
+ static Result *lastResult() { return m_lastResult; }
+ static void setLastResult(const Result &result)
+ {
+ *m_lastResult = result;
+ }
+
+private:
+ NodePtr m_node;
+ Type m_type;
+ QString m_message;
+ static Result *m_lastResult;
+};
+
+#endif /* ABAKUS_RESULT_H */
+
+// vim: set et ts=8 sw=4:
diff --git a/src/resultlistview.cpp b/src/resultlistview.cpp
new file mode 100644
index 0000000..abe76c1
--- /dev/null
+++ b/src/resultlistview.cpp
@@ -0,0 +1,149 @@
+/*
+ * resultlistview.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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+#include <kdebug.h>
+#include <kpopupmenu.h>
+#include <klocale.h>
+
+#include <qclipboard.h>
+#include <qapplication.h>
+#include <qevent.h>
+#include <qcursor.h>
+#include <qdragobject.h>
+#include <qheader.h>
+
+#include "resultlistview.h"
+#include "resultlistviewtext.h"
+#include "dragsupport.h"
+
+using DragSupport::makePixmap;
+using namespace ResultList;
+
+ResultListView::ResultListView(QWidget *parent, const char *name) :
+ KListView(parent, name), m_itemRightClicked(0)
+{
+ connect(this, SIGNAL(doubleClicked(QListViewItem *, const QPoint &, int)),
+ SLOT(slotDoubleClicked(QListViewItem *, const QPoint &, int)));
+
+ addColumn(i18n("Expression"));
+ addColumn(i18n("Result"));
+ addColumn(i18n("Shortcut"));
+
+ header()->hide(); // I hate that header
+ header()->setStretchEnabled(ResultColumn, true);
+
+ setDragEnabled(true);
+ setItemMargin(2);
+ setColumnAlignment(ResultColumn, AlignLeft);
+ setColumnAlignment(ShortcutColumn, AlignHCenter);
+ setColumnWidthMode(ResultColumn, Maximum);
+ setSortColumn(-1);
+}
+
+bool ResultListView::getStackValue(unsigned stackPosition, Abakus::number_t &result)
+{
+ QListViewItem *it = firstChild();
+ for(; it; it = it->itemBelow()) {
+ ResultListViewText *resultItem = dynamic_cast<ResultListViewText *>(it);
+ if(!resultItem->wasError() && resultItem->stackPosition() == stackPosition) {
+ result = Abakus::number_t(resultItem->resultText().latin1());
+ return true;
+ }
+ }
+
+ return false;
+}
+
+QDragObject *ResultListView::dragObject()
+{
+ QPoint viewportPos = viewport()->mapFromGlobal(QCursor::pos());
+ ResultListViewText *item = itemUnderCursor();
+
+ if(item) {
+ QString text = item->resultText();
+
+ int column = header()->sectionAt(viewportPos.x());
+
+ if(column == ExpressionColumn)
+ text = item->expressionText();
+
+ QDragObject *drag = new QTextDrag(text, this);
+ drag->setPixmap(makePixmap(text, font()));
+
+ return drag;
+ }
+
+ return 0;
+}
+
+void ResultListView::contextMenuEvent(QContextMenuEvent *e)
+{
+ m_itemRightClicked = itemUnderCursor();
+ KPopupMenu *menu = constructPopupMenu(m_itemRightClicked);
+
+ menu->popup(e->globalPos());
+}
+
+void ResultListView::slotDoubleClicked(QListViewItem *item, const QPoint &, int c)
+{
+ ResultListViewText *textItem = dynamic_cast<ResultListViewText *>(item);
+ if(!textItem)
+ return;
+
+ if(c == ExpressionColumn)
+ emit signalEntrySelected(textItem->expressionText());
+ else if(c == ResultColumn)
+ emit signalResultSelected(textItem->resultText());
+}
+
+KPopupMenu *ResultListView::constructPopupMenu(const ResultListViewText *item)
+{
+ KPopupMenu *menu = new KPopupMenu(this, "list view context menu");
+
+ menu->insertItem(i18n("Clear &History"), this, SLOT(clear()), ALT+Key_R);
+
+ int id = menu->insertItem(i18n("Copy Result to Clipboard"), this, SLOT(slotCopyResult()));
+ if(!item || item->wasError())
+ menu->setItemEnabled(id, false);
+
+ return menu;
+}
+
+void ResultListView::slotCopyResult()
+{
+ if(!m_itemRightClicked)
+ return;
+
+ QClipboard *clipboard = QApplication::clipboard();
+
+ clipboard->setText(m_itemRightClicked->resultText(), QClipboard::Clipboard);
+}
+
+ResultListViewText *ResultListView::lastItem() const
+{
+ return static_cast<ResultListViewText *>(KListView::lastItem());
+}
+
+ResultListViewText *ResultListView::itemUnderCursor() const
+{
+ QPoint viewportPos = viewport()->mapFromGlobal(QCursor::pos());
+ QListViewItem *underCursor = itemAt(viewportPos);
+ return static_cast<ResultListViewText *>(underCursor);
+}
+
+#include "resultlistview.moc"
diff --git a/src/resultlistview.h b/src/resultlistview.h
new file mode 100644
index 0000000..891443c
--- /dev/null
+++ b/src/resultlistview.h
@@ -0,0 +1,64 @@
+#ifndef ABAKUS_RESULTLISTVIEW_H
+#define ABAKUS_RESULTLISTVIEW_H
+/*
+ * resultlistview.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include <klistview.h>
+#include "numerictypes.h"
+
+class KPopupMenu;
+class QLabel;
+class QDragObject;
+class ResultListViewText;
+
+namespace ResultList {
+ enum { ExpressionColumn = 0, ResultColumn, ShortcutColumn };
+}
+
+class ResultListView : public KListView
+{
+ Q_OBJECT
+
+ public:
+ ResultListView(QWidget *parent = 0, const char *name = "result list view");
+
+ bool getStackValue(unsigned stackPosition, Abakus::number_t &result);
+
+ ResultListViewText *lastItem() const;
+
+ protected:
+ virtual void contextMenuEvent(QContextMenuEvent *e);
+ virtual QDragObject *dragObject();
+
+ signals:
+ void signalEntrySelected(const QString &text);
+ void signalResultSelected(const QString &text);
+
+ private slots:
+ void slotDoubleClicked(QListViewItem *item, const QPoint & /* Ignored */, int c);
+ void slotCopyResult();
+
+ private:
+ KPopupMenu *constructPopupMenu(const ResultListViewText *item);
+ ResultListViewText *itemUnderCursor() const;
+
+ ResultListViewText *m_itemRightClicked;
+};
+
+#endif
diff --git a/src/resultlistviewtext.cpp b/src/resultlistviewtext.cpp
new file mode 100644
index 0000000..cbf7bfb
--- /dev/null
+++ b/src/resultlistviewtext.cpp
@@ -0,0 +1,135 @@
+/*
+ * resultlistviewtext.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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+#include <kdebug.h>
+
+#include <qregexp.h>
+#include <qpainter.h>
+#include <qfontmetrics.h>
+#include <qfont.h>
+#include <qpalette.h>
+
+#include "resultlistviewtext.h"
+
+using namespace ResultList;
+
+ResultListViewText::ResultListViewText(KListView *listView,
+ const QString &text,
+ const QString &result,
+ ResultListViewText *after,
+ bool isError)
+ : KListViewItem(listView, after, text, result), m_text(text),
+ m_result(result), m_wasError(isError), m_stackPosition(0)
+{
+ // This is some kind of non-result answer, don't bother worrying about the
+ // stack status, it hasn't changed.
+}
+
+ResultListViewText::ResultListViewText(KListView *listView,
+ const QString &text,
+ const Abakus::number_t &result,
+ ResultListViewText *after,
+ bool isError)
+ : KListViewItem(listView, after, text), m_text(text),
+ m_result(result.toString()), m_wasError(isError), m_stackPosition(0),
+ m_value(result)
+{
+ if(after) {
+ ResultListViewText *item = static_cast<ResultListViewText *>(listView->firstChild());
+ for (; item && item != this; item = static_cast<ResultListViewText *>(item->itemBelow())) {
+ if(!item->wasError()) {
+ item->setStackPosition(item->stackPosition() + 1);
+ item->repaint();
+ }
+ }
+ }
+
+ setStackPosition(0);
+
+ // Call this manually to be rid of trailing zeroes.
+ setText(ResultColumn, m_value.toString());
+}
+
+void ResultListViewText::setStackPosition(unsigned pos)
+{
+ setText(ShortcutColumn, QString("$%1").arg(pos));
+ m_stackPosition = pos;
+}
+
+void ResultListViewText::precisionChanged()
+{
+ if(m_wasError)
+ return;
+
+ m_result = m_value.toString();
+ setText(ResultColumn, m_result);
+}
+
+void ResultListViewText::paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align)
+{
+ QColorGroup group(cg);
+
+ // XXX: The Qt::red may not provide good contrast with weird color schemes.
+ // If so I apologize.
+ if(m_wasError && column == ResultColumn)
+ group.setColor(QColorGroup::Text, m_result == "OK" ? cg.link() : Qt::red);
+
+ if(column == ResultColumn) {
+ QFont f = p->font();
+ f.setBold(true);
+ p->setFont(f);
+ }
+
+ if(column == ShortcutColumn) {
+ QFont f = p->font();
+ f.setItalic(true);
+ f.setPointSize(QMIN(f.pointSize() * 9 / 11, 10));
+ p->setFont(f);
+ }
+
+ KListViewItem::paintCell(p, group, column, width, align);
+}
+
+int ResultListViewText::width(const QFontMetrics &fm, const QListView *lv, int c) const
+{
+ // Simulate painting the text to get accurate results.
+ if(c == ResultColumn) {
+ QFont f = lv->font();
+ f.setBold(true);
+ return KListViewItem::width(QFontMetrics(f), lv, c);
+ }
+
+ if(c == ShortcutColumn) {
+ QFont f = lv->font();
+ f.setItalic(true);
+ f.setPointSize(QMIN(f.pointSize() * 9 / 11, 10));
+ return KListViewItem::width(QFontMetrics(f), lv, c);
+ }
+
+ return KListViewItem::width(fm, lv, c);
+}
+
+void ResultListViewText::setText(int column, const QString &text)
+{
+ if(!m_wasError && column == ResultColumn) {
+ KListViewItem::setText(column, m_value.toString());
+ return;
+ }
+
+ KListViewItem::setText(column, text);
+}
diff --git a/src/resultlistviewtext.h b/src/resultlistviewtext.h
new file mode 100644
index 0000000..d624b0b
--- /dev/null
+++ b/src/resultlistviewtext.h
@@ -0,0 +1,70 @@
+#ifndef ABAKUS_RESULTLISTVIEWTEXT_H
+#define ABAKUS_RESULTLISTVIEWTEXT_H
+/*
+ * resultlistviewtext.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include "resultlistview.h"
+#include "numerictypes.h"
+
+class QPainter;
+class QColorGroup;
+class QFontMetrics;
+
+// This class shows the results shown in the MainWindow result pane.
+class ResultListViewText : public KListViewItem
+{
+ public:
+ ResultListViewText(KListView *listView,
+ const QString &text,
+ const QString &result,
+ ResultListViewText *after,
+ bool isError = false);
+
+ ResultListViewText(KListView *listView,
+ const QString &text,
+ const Abakus::number_t &result,
+ ResultListViewText *after,
+ bool isError = false);
+
+ QString expressionText() const { return m_text; }
+ QString resultText() const { return m_result; }
+
+ bool wasError() const { return m_wasError; }
+ unsigned stackPosition() const { return m_stackPosition; }
+
+ void setStackPosition(unsigned pos);
+
+ // Redisplays the text by calling value.toString again.
+ void precisionChanged();
+
+ // Reimplemented from KListViewItem
+ virtual void paintCell(QPainter *p, const QColorGroup &cg, int column, int width, int align);
+ virtual int width(const QFontMetrics &fm, const QListView *lv, int c) const;
+
+ // Reimplemented to remove trailing zeroes from results.
+ virtual void setText(int column, const QString &text);
+
+ private:
+ QString m_text, m_result;
+ bool m_wasError;
+ unsigned m_stackPosition;
+ Abakus::number_t m_value;
+};
+
+#endif
diff --git a/src/rpnmuncher.cpp b/src/rpnmuncher.cpp
new file mode 100644
index 0000000..ad75495
--- /dev/null
+++ b/src/rpnmuncher.cpp
@@ -0,0 +1,267 @@
+/*
+ * rpnmuncher.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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+#include <math.h>
+
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qvaluestack.h>
+#include <qregexp.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+#include "rpnmuncher.h"
+#include "valuemanager.h"
+#include "function.h"
+
+/**
+ * Holds either a textual identifier, or a numeric value.
+ */
+class Operand
+{
+ public:
+ Operand() : m_isValue(true), m_value(0) { }
+ Operand(const QString &ident) : m_isValue(false), m_text(ident) { }
+ Operand(Abakus::number_t value) : m_isValue(true), m_value(value) { }
+
+ Abakus::number_t value() const
+ {
+ if(m_isValue)
+ return m_value;
+
+ return ValueManager::instance()->value(m_text);
+ }
+
+ operator Abakus::number_t() const
+ {
+ return value();
+ }
+
+ QString text() const { return m_text; }
+
+ private:
+ bool m_isValue;
+ QString m_text;
+ Abakus::number_t m_value;
+};
+
+typedef enum { Number = 256, Func, Ident, Power, Set, Remove, Pop, Clear, Unknown } Token;
+
+static int tokenize (const QString &token);
+
+QString RPNParser::m_errorStr;
+bool RPNParser::m_error(false);
+OperandStack RPNParser::m_stack;
+
+struct Counter
+{
+ ~Counter() {
+ Abakus::number_t count( static_cast<int>(RPNParser::stack().count()) );
+ ValueManager::instance()->setValue("stackCount", count);
+ }
+};
+
+Abakus::number_t RPNParser::rpnParseString(const QString &text)
+{
+ QStringList tokens = QStringList::split(QRegExp("\\s"), text);
+ Counter counter; // Will update stack count when we leave proc.
+ (void) counter; // Avoid warnings about it being unused.
+
+ // Used in the case statements below
+ Operand l, r;
+ FunctionManager *manager = FunctionManager::instance();
+ Function *fn = 0;
+
+ m_error = false;
+ m_errorStr = QString::null;
+
+ for(QStringList::ConstIterator it = tokens.begin(); it != tokens.end(); ++it) {
+ switch(tokenize(*it))
+ {
+ case Number:
+ m_stack.push(Abakus::number_t((*it).latin1()));
+ break;
+
+ case Pop:
+ if(m_stack.isEmpty()) {
+ m_error = true;
+ m_errorStr = i18n("Can't pop from an empty stack.");
+ return Abakus::number_t::nan();
+ }
+
+ m_stack.pop();
+ break;
+
+ case Clear:
+ m_stack.clear();
+ break;
+
+ case Func:
+ if(m_stack.count() < 1) {
+ m_error = true;
+ m_errorStr = i18n("Insufficient operands for function %1").arg(*it);
+ return Abakus::number_t::nan();
+ }
+
+ fn = manager->function(*it);
+
+ l = m_stack.pop();
+ m_stack.push(evaluateFunction(fn, l));
+ break;
+
+ case Ident:
+ m_stack.push(*it);
+ break;
+
+ case Set:
+ case Remove:
+ m_error = true;
+ m_errorStr = i18n("The set and remove commands can only be used in normal mode.");
+ return Abakus::number_t::nan();
+ break;
+
+ case Power:
+ if(m_stack.count() < 2) {
+ m_error = true;
+ m_errorStr = i18n("Insufficient operands for exponentiation operator.");
+ return Abakus::number_t::nan();
+ }
+
+ r = m_stack.pop();
+ l = m_stack.pop();
+ m_stack.push(l.value().pow(r));
+ break;
+
+ case Unknown:
+ m_error = true;
+ m_errorStr = i18n("Unknown token %1").arg(*it);
+ return Abakus::number_t::nan();
+ break;
+
+ case '=':
+ r = m_stack.pop();
+ l = m_stack.pop();
+ ValueManager::instance()->setValue(l.text(), r);
+
+ m_stack.push(l);
+ break;
+
+ case '+':
+ if(m_stack.count() < 2) {
+ m_error = true;
+ m_errorStr = i18n("Insufficient operands for addition operator.");
+ return Abakus::number_t::nan();
+ }
+
+ r = m_stack.pop();
+ l = m_stack.pop();
+ m_stack.push(l.value() + r.value());
+ break;
+
+ case '-':
+ if(m_stack.count() < 2) {
+ m_error = true;
+ m_errorStr = i18n("Insufficient operands for subtraction operator.");
+ return Abakus::number_t::nan();
+ }
+
+ r = m_stack.pop();
+ l = m_stack.pop();
+ m_stack.push(l.value() - r.value());
+ break;
+
+ case '*':
+ if(m_stack.count() < 2) {
+ m_error = true;
+ m_errorStr = i18n("Insufficient operands for multiplication operator.");
+ return Abakus::number_t::nan();
+ }
+
+ r = m_stack.pop();
+ l = m_stack.pop();
+ m_stack.push(l.value() * r.value());
+ break;
+
+ case '/':
+ if(m_stack.count() < 2) {
+ m_error = true;
+ m_errorStr = i18n("Insufficient operands for division operator.");
+ return Abakus::number_t::nan();
+ }
+
+ r = m_stack.pop();
+ l = m_stack.pop();
+ m_stack.push(l.value() / r.value());
+ break;
+
+ default:
+ // Impossible case happened.
+ kdError() << "Impossible case happened in " << k_funcinfo << endl;
+ m_error = true;
+ m_errorStr = "Bug found in program, please report.";
+ return Abakus::number_t::nan();
+ }
+ }
+
+ // TODO: Should this be an error?
+ if(m_stack.isEmpty())
+ return Abakus::number_t::nan();
+
+ return m_stack.top();
+}
+
+static int tokenize (const QString &token)
+{
+ bool isOK;
+
+ token.toDouble(&isOK);
+ if(isOK)
+ return Number;
+
+ if(token == "**" || token == "^")
+ return Power;
+
+ if(FunctionManager::instance()->isFunction(token))
+ return Func;
+
+ if(token.lower() == "set")
+ return Set;
+
+ if(token.lower() == "pop")
+ return Pop;
+
+ if(token.lower() == "clear")
+ return Clear;
+
+ if(token.lower() == "remove")
+ return Remove;
+
+ if(QRegExp("^\\w+$").search(token) != -1 &&
+ QRegExp("\\d").search(token) == -1)
+ {
+ return Ident;
+ }
+
+ if(QRegExp("^[-+*/=]$").search(token) != -1)
+ return token[0];
+
+ return Unknown;
+}
+
+// vim: set et sw=4 ts=8:
diff --git a/src/rpnmuncher.h b/src/rpnmuncher.h
new file mode 100644
index 0000000..71c340e
--- /dev/null
+++ b/src/rpnmuncher.h
@@ -0,0 +1,44 @@
+#ifndef ABAKUS_RPNMUNCHER_H
+#define ABAKUS_RPNMUNCHER_H
+/*
+ * rpnmuncher.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+class QString;
+class Operand;
+
+template<class T> class QValueStack;
+typedef QValueStack<Operand> OperandStack;
+
+#include "numerictypes.h"
+
+class RPNParser
+{
+ public:
+ static Abakus::number_t rpnParseString(const QString &text);
+ static bool wasError() { return m_error; }
+ static QString errorString() { return m_errorStr; }
+ static OperandStack &stack() { return m_stack; }
+
+ private:
+ static QString m_errorStr;
+ static bool m_error;
+ static OperandStack m_stack;
+};
+
+#endif
diff --git a/src/sharedptr.h b/src/sharedptr.h
new file mode 100644
index 0000000..2837870
--- /dev/null
+++ b/src/sharedptr.h
@@ -0,0 +1,122 @@
+#ifndef ABAKUS_SHARED_PTR_H
+#define ABAKUS_SHARED_PTR_H
+/*
+ * sharedptr.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include <kdebug.h>
+
+template <class T>
+class SharedPtr
+{
+public:
+ typedef T value_type;
+
+ class Data
+ {
+ public:
+ Data(T *ptr = 0, unsigned refCount = 1) : m_ptr(ptr), m_refCount(refCount)
+ {
+ }
+
+ void deref()
+ {
+ --m_refCount;
+
+ if(!m_refCount) {
+ delete m_ptr;
+ m_ptr = 0;
+ }
+ }
+
+ void ref()
+ {
+ ++m_refCount;
+ }
+
+ T *ptr() { return m_ptr; }
+ const T* ptr() const { return m_ptr; }
+
+ unsigned refCount() const { return m_refCount; }
+
+ private:
+ T *m_ptr;
+ unsigned m_refCount;
+ };
+
+ SharedPtr() : m_data(new Data)
+ {
+ }
+
+ SharedPtr(T* ptr) : m_data(new Data(ptr))
+ {
+ }
+
+ SharedPtr(const SharedPtr<T> &other) : m_data(other.m_data)
+ {
+ m_data->ref();
+ }
+
+ ~SharedPtr()
+ {
+ m_data->deref();
+ }
+
+ SharedPtr<T> &operator=(const SharedPtr<T> &other)
+ {
+ if(&other == this)
+ return *this;
+
+ m_data->deref();
+ m_data = other.m_data;
+ m_data->ref();
+
+ return *this;
+ }
+
+ T *operator ->()
+ {
+ return m_data->ptr();
+ }
+
+ const T *operator ->() const
+ {
+ return m_data->ptr();
+ }
+
+ T &operator *()
+ {
+ return *m_data->ptr();
+ }
+
+ const T& operator *() const
+ {
+ return *m_data->ptr();
+ }
+
+ bool isNull() const { return m_data->ptr() == 0; }
+
+ unsigned refCount() const { return m_data->refCount(); }
+
+private:
+ Data *m_data;
+};
+
+#endif /* ABAKUS_SHARED_PTR_H */
+
+// vim: set et ts=8 sw=4:
diff --git a/src/valuemanager.cpp b/src/valuemanager.cpp
new file mode 100644
index 0000000..db8ef99
--- /dev/null
+++ b/src/valuemanager.cpp
@@ -0,0 +1,105 @@
+/*
+ * valuemanager.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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+#include <kdebug.h>
+#include <klocale.h>
+
+#include <qregexp.h>
+
+#include "numerictypes.h"
+#include "valuemanager.h"
+
+ValueManager *ValueManager::m_manager = 0;
+
+ValueManager *ValueManager::instance()
+{
+ if(!m_manager)
+ m_manager = new ValueManager;
+
+ return m_manager;
+}
+
+ValueManager::ValueManager(QObject *parent, const char *name) :
+ QObject(parent, name)
+{
+ m_values.insert("pi", Abakus::number_t::PI);
+ m_values.insert("e", Abakus::number_t::E);
+}
+
+Abakus::number_t ValueManager::value(const QString &name) const
+{
+ return m_values[name];
+}
+
+bool ValueManager::isValueSet(const QString &name) const
+{
+ return m_values.contains(name);
+}
+
+bool ValueManager::isValueReadOnly(const QString &name) const
+{
+ QRegExp readOnlyValues("^(ans|pi|e|stackCount)$");
+
+ return name.find(readOnlyValues) != -1;
+}
+
+void ValueManager::setValue(const QString &name, const Abakus::number_t value)
+{
+ if(m_values.contains(name) && this->value(name) != value)
+ emit signalValueChanged(name, value);
+ else if(!m_values.contains(name))
+ emit signalValueAdded(name, value);
+
+ m_values.replace(name, value);
+}
+
+void ValueManager::removeValue(const QString &name)
+{
+ if(m_values.contains(name))
+ emit signalValueRemoved(name);
+
+ m_values.remove(name);
+}
+
+void ValueManager::slotRemoveUserVariables()
+{
+ QStringList vars = valueNames();
+
+ for(QStringList::ConstIterator var = vars.constBegin(); var != vars.constEnd(); ++var)
+ if(!isValueReadOnly(*var))
+ removeValue(*var);
+}
+
+QStringList ValueManager::valueNames() const
+{
+ return m_values.keys();
+}
+
+QString ValueManager::description(const QString &valueName)
+{
+ if(valueName == "e")
+ return i18n("Natural exponential base - 2.7182818");
+ if(valueName == "pi")
+ return i18n("pi (π) - 3.1415926");
+
+ return QString();
+}
+
+#include "valuemanager.moc"
+
+// vim: set et ts=8 sw=4 encoding=utf-8:
diff --git a/src/valuemanager.h b/src/valuemanager.h
new file mode 100644
index 0000000..f57016c
--- /dev/null
+++ b/src/valuemanager.h
@@ -0,0 +1,69 @@
+#ifndef ABAKUS_VALUEMANAGER_H
+#define ABAKUS_VALUEMANAGER_H
+/*
+ * valuemanager.h - 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., 59 Temple Place, Suite 330, Boston, MA 02110-1301 USA
+ */
+
+#include <qobject.h>
+#include <qmap.h>
+#include <qstring.h>
+#include <qstringlist.h>
+
+#include "numerictypes.h"
+
+class ValueManager : public QObject
+{
+ Q_OBJECT
+ public:
+ typedef QMap<QString, Abakus::number_t> valueMap;
+
+ static ValueManager *instance();
+
+ Abakus::number_t value(const QString &name) const;
+
+ bool isValueSet(const QString &name) const;
+ bool isValueReadOnly(const QString &name) const;
+
+ void setValue(const QString &name, const Abakus::number_t value);
+ void removeValue(const QString &name);
+
+ QStringList valueNames() const;
+
+ /**
+ * Returns a textual description of a constant built-into abakus.
+ */
+ static QString description(const QString &valueName);
+
+ signals:
+ void signalValueAdded(const QString &name, Abakus::number_t value);
+ void signalValueRemoved(const QString &name);
+ void signalValueChanged(const QString &name, Abakus::number_t newValue);
+
+ public slots:
+ void slotRemoveUserVariables();
+
+ private:
+ ValueManager(QObject *parent = 0, const char *name = "value manager");
+
+ static ValueManager *m_manager;
+ valueMap m_values;
+};
+
+#endif
+
+// vim: set et sw=4 ts=8: