[gcalctool/vala] Port to Vala
- From: Robert Ancell <rancell src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gcalctool/vala] Port to Vala
- Date: Sun, 7 Oct 2012 21:53:08 +0000 (UTC)
commit d44f190307fb84230fdc51ef6736fa5a9b2d76c2
Author: Robert Ancell <robert ancell canonical com>
Date: Tue Sep 11 10:35:32 2012 +1200
Port to Vala
.gitignore | 9 +-
configure.ac | 12 +-
data/Makefile.am | 1 -
data/buttons-advanced.ui | 47 -
data/buttons-basic.ui | 23 -
data/buttons-financial.ui | 74 -
data/buttons-programming.ui | 116 --
data/preferences.ui | 276 ----
po/POTFILES.in | 39 +-
src/Makefile.am | 213 +--
src/config.vapi | 6 +
src/currency-manager.c | 626 --------
src/currency-manager.h | 47 -
src/currency.c | 96 --
src/currency.h | 54 -
src/currency.vala | 499 +++++++
src/equation-lexer.vala | 681 +++++++++
src/equation-parser.vala | 1703 ++++++++++++++++++++++
src/equation.vala | 323 +++++
src/financial.c | 286 ----
src/financial.h | 33 -
src/financial.vala | 249 ++++
src/gcalccmd.c | 98 --
src/gcalccmd.vala | 58 +
src/gcalctool.c | 578 --------
src/gcalctool.vala | 413 ++++++
src/lexer.c | 587 --------
src/lexer.h | 40 -
src/math-buttons.c | 1361 ------------------
src/math-buttons.h | 56 -
src/math-buttons.vala | 1008 +++++++++++++
src/math-converter.c | 440 ------
src/math-converter.h | 53 -
src/math-converter.vala | 279 ++++
src/math-display.c | 451 ------
src/math-display.h | 46 -
src/math-display.vala | 337 +++++
src/math-equation.c | 1948 -------------------------
src/math-equation.h | 128 --
src/math-equation.vala | 1191 ++++++++++++++++
src/math-preferences.c | 395 ------
src/math-preferences.h | 41 -
src/math-preferences.vala | 301 ++++
src/math-variable-popup.c | 308 ----
src/math-variable-popup.h | 40 -
src/math-variable-popup.vala | 184 +++
src/math-variables.c | 179 ---
src/math-variables.h | 48 -
src/math-variables.vala | 114 ++
src/math-window.c | 246 ----
src/math-window.h | 53 -
src/math-window.vala | 117 ++
src/mp-binary.c | 221 ---
src/mp-convert.c | 717 ----------
src/mp-enums.c.template | 36 -
src/mp-enums.h.template | 25 -
src/mp-equation.c | 315 ----
src/mp-equation.h | 69 -
src/mp-private.h | 39 -
src/mp-serializer.c | 617 --------
src/mp-serializer.h | 79 -
src/mp-trigonometric.c | 628 --------
src/mp.c | 2096 ---------------------------
src/mp.h | 330 -----
src/number.vala | 3223 ++++++++++++++++++++++++++++++++++++++++++
src/parser.c | 1228 ----------------
src/parser.h | 79 -
src/parserfunc.c | 967 -------------
src/parserfunc.h | 80 --
src/prelexer.c | 214 ---
src/prelexer.h | 93 --
src/serializer.vala | 445 ++++++
src/test-equation.vala | 601 ++++++++
src/test-mp-equation.c | 630 ---------
src/test-mp.c | 240 ----
src/test-number.vala | 1005 +++++++++++++
src/unit-category.c | 136 --
src/unit-category.h | 56 -
src/unit-manager.c | 268 ----
src/unit-manager.h | 54 -
src/unit.c | 212 ---
src/unit.h | 60 -
src/unit.vala | 327 +++++
83 files changed, 13158 insertions(+), 18443 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 3cc5d51..8f5311e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -17,6 +17,7 @@ Makefile.in
Makefile.in.in
.deps
stamp-h1
+*.stamp-t
xmldocs.make
data/gcalctool.desktop
data/org.gnome.gcalctool.gschema.xml
@@ -25,18 +26,14 @@ help/[^C]*/*.page
help/[^C]*/legal.xml
help/*/*.gmo
help/*/*.mo
+help/*/*.stamp
po/POTFILES
po/stamp-it
po/*.gmo
po/.intltool-merge-cache
po/*.pot
src/*.o
-src/mp-enums.c
-src/mp-enums.h
-src/mp-equation-lexer.c
-src/mp-equation-lexer.h
-src/mp-equation-parser.c
-src/mp-equation-parser.h
+src/*.c
src/gcalccmd
src/gcalctool
src/test-mp
diff --git a/configure.ac b/configure.ac
index 5e9a833..f340969 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,19 +1,18 @@
dnl Process this file with autoconf to produce a configure script.
AC_INIT(configure.ac)
-AM_INIT_AUTOMAKE(gcalctool, 6.5.91)
+AM_INIT_AUTOMAKE(gcalctool, 6.6.0)
AM_CONFIG_HEADER(config.h)
AM_MAINTAINER_MODE
GNOME_MAINTAINER_MODE_DEFINES
m4_ifdef([AM_SILENT_RULES],[AM_SILENT_RULES([yes])])
AC_ISC_POSIX
-AC_PROG_CC
-AM_PROG_CC_STDC
+AM_PROG_VALAC([0.16.0])
+AM_PROG_CC_C_O
AC_HEADER_STDC
GLIB_GSETTINGS
-GNOME_COMPILE_WARNINGS(maximum)
dnl ###########################################################################
dnl Dependencies
@@ -37,11 +36,6 @@ PKG_CHECK_MODULES(GCALCCMD, [
libxml-2.0
])
-GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
-AC_SUBST(GLIB_MKENUMS)
-
-AC_CHECK_LIB(m, log)
-
dnl ###########################################################################
dnl Internationalization
dnl ###########################################################################
diff --git a/data/Makefile.am b/data/Makefile.am
index 9a02223..ee552d0 100644
--- a/data/Makefile.am
+++ b/data/Makefile.am
@@ -1,6 +1,5 @@
uidir = $(datadir)/gcalctool
ui_DATA = \
- preferences.ui \
buttons-basic.ui \
buttons-advanced.ui \
buttons-financial.ui \
diff --git a/data/buttons-advanced.ui b/data/buttons-advanced.ui
index ac94ecf..c37d87b 100644
--- a/data/buttons-advanced.ui
+++ b/data/buttons-advanced.ui
@@ -20,7 +20,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the inverse button">Inverse</property>
</object>
</child>
- <signal name="clicked" handler="button_cb" swapped="no"/>
<child>
<object class="GtkLabel" id="label20">
<property name="visible">True</property>
@@ -52,7 +51,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the factorize button">Factorize</property>
</object>
</child>
- <signal name="clicked" handler="factorize_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">5</property>
@@ -74,7 +72,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the factorial button">Factorial</property>
</object>
</child>
- <signal name="clicked" handler="button_cb" swapped="no"/>
<child>
<object class="GtkLabel" id="label14">
<property name="visible">True</property>
@@ -100,7 +97,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">7</property>
@@ -118,7 +114,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
<child>
<object class="GtkLabel" id="imaginary_label">
<property name="visible">True</property>
@@ -144,7 +139,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="top_attach">2</property>
@@ -161,7 +155,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="top_attach">1</property>
@@ -178,7 +171,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -197,7 +189,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -216,7 +207,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -235,7 +225,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -254,7 +243,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -273,7 +261,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="top_attach">3</property>
@@ -290,7 +277,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -310,7 +296,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="top_attach">4</property>
@@ -327,7 +312,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="numeric_point_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -346,7 +330,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -365,7 +348,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -384,7 +366,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="subtract_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -403,7 +384,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -422,7 +402,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="solve_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">4</property>
@@ -440,7 +419,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="clear_cb" swapped="no"/>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
@@ -466,7 +444,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the subscript mode button">Subscript</property>
</object>
</child>
- <signal name="clicked" handler="set_subscript_cb" swapped="no"/>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
@@ -491,7 +468,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the superscript mode button">Superscript</property>
</object>
</child>
- <signal name="clicked" handler="set_superscript_cb" swapped="no"/>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
@@ -519,7 +495,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the scientific exponent button">Scientific Exponent</property>
</object>
</child>
- <signal name="clicked" handler="exponent_cb" swapped="no"/>
<child>
<object class="GtkLabel" id="label2">
<property name="visible">True</property>
@@ -543,7 +518,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -560,7 +534,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">4</property>
@@ -579,7 +552,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">5</property>
@@ -601,7 +573,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the memory button">Memory</property>
</object>
</child>
- <signal name="clicked" handler="memory_cb" swapped="no"/>
<child>
<object class="GtkHBox" id="hbox20">
<property name="visible">True</property>
@@ -651,7 +622,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the absolute value button">Absolute Value</property>
</object>
</child>
- <signal name="clicked" handler="button_cb" swapped="no"/>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
@@ -677,7 +647,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">6</property>
@@ -696,7 +665,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">8</property>
@@ -715,7 +683,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">9</property>
@@ -734,7 +701,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">9</property>
@@ -752,7 +718,6 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">8</property>
@@ -774,7 +739,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the exponentiation (x to the power of y) button">Exponent</property>
</object>
</child>
- <signal name="clicked" handler="button_cb" swapped="no"/>
<child>
<object class="GtkLabel" id="x_pow_y_label">
<property name="visible">True</property>
@@ -800,7 +764,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">7</property>
@@ -818,7 +781,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="undo_cb" swapped="no"/>
<child>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
@@ -840,7 +802,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
<child>
<object class="GtkLabel" id="pi_label">
<property name="visible">True</property>
@@ -865,7 +826,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
<child>
<object class="GtkLabel" id="eulers_number_label">
<property name="visible">True</property>
@@ -896,7 +856,6 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
@@ -910,7 +869,6 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -926,7 +884,6 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -942,7 +899,6 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="top_attach">1</property>
@@ -958,7 +914,6 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -976,7 +931,6 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -1005,7 +959,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the store value button">Store</property>
</object>
</child>
- <signal name="clicked" handler="function_cb" swapped="no"/>
<child>
<object class="GtkHBox" id="hbox2">
<property name="visible">True</property>
diff --git a/data/buttons-basic.ui b/data/buttons-basic.ui
index c8b1de6..4515b7c 100644
--- a/data/buttons-basic.ui
+++ b/data/buttons-basic.ui
@@ -17,7 +17,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="top_attach">1</property>
@@ -34,7 +33,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
@@ -49,7 +47,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -66,7 +63,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -83,7 +79,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -102,7 +97,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -121,7 +115,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -138,7 +131,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="top_attach">2</property>
@@ -155,7 +147,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -175,7 +166,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="top_attach">3</property>
@@ -192,7 +182,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="numeric_point_cb"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -211,7 +200,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -230,7 +218,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -249,7 +236,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -268,7 +254,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="subtract_cb"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -287,7 +272,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -306,7 +290,6 @@
<property name="receives_default">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="solve_cb"/>
</object>
<packing>
<property name="left_attach">4</property>
@@ -325,7 +308,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">4</property>
@@ -344,7 +326,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">5</property>
@@ -362,7 +343,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="clear_cb"/>
<child>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
@@ -385,7 +365,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">5</property>
@@ -407,7 +386,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the exponentiation (x to the power of y) button">Exponent</property>
</object>
</child>
- <signal name="clicked" handler="button_cb"/>
<child>
<object class="GtkLabel" id="x_squared_label">
<property name="visible">True</property>
@@ -432,7 +410,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="undo_cb"/>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
diff --git a/data/buttons-financial.ui b/data/buttons-financial.ui
index 1629924..141f563 100644
--- a/data/buttons-financial.ui
+++ b/data/buttons-financial.ui
@@ -8,7 +8,6 @@
<property name="title" translatable="yes" comments="Title of Compounding Term dialog">Compounding Term</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
- <signal name="response" handler="finc_response_cb" swapped="no"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox1">
<property name="visible">True</property>
@@ -73,7 +72,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -89,7 +87,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -105,7 +102,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -196,7 +192,6 @@
<property name="title" translatable="yes" comments="Title of Double-Declining Depreciation dialog">Double-Declining Depreciation</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
- <signal name="response" handler="finc_response_cb" swapped="no"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox2">
<property name="visible">True</property>
@@ -306,7 +301,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -322,7 +316,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -354,7 +347,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -384,7 +376,6 @@
<property name="title" translatable="yes" comments="Title of Future Value dialog">Future Value</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
- <signal name="response" handler="finc_response_cb" swapped="no"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox3">
<property name="visible">True</property>
@@ -510,7 +501,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -526,7 +516,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -542,7 +531,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -572,7 +560,6 @@
<property name="title" translatable="yes" comments="Title of Gross Profit Margin dialog">Gross Profit Margin</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
- <signal name="response" handler="finc_response_cb" swapped="no"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox10">
<property name="visible">True</property>
@@ -681,7 +668,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -697,7 +683,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -727,7 +712,6 @@
<property name="title" translatable="yes" comments="Title of Periodic Payment dialog">Periodic Payment</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
- <signal name="response" handler="finc_response_cb" swapped="no"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox4">
<property name="visible">True</property>
@@ -854,7 +838,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -870,7 +853,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -886,7 +868,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -916,7 +897,6 @@
<property name="title" translatable="yes" comments="Title of Present Value dialog">Present Value</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
- <signal name="response" handler="finc_response_cb" swapped="no"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox5">
<property name="visible">True</property>
@@ -1043,7 +1023,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1059,7 +1038,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1075,7 +1053,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1105,7 +1082,6 @@
<property name="title" translatable="yes" comments="Title of Periodic Interest Rate dialog">Periodic Interest Rate</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
- <signal name="response" handler="finc_response_cb" swapped="no"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox6">
<property name="visible">True</property>
@@ -1232,7 +1208,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1248,7 +1223,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1264,7 +1238,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1294,7 +1267,6 @@
<property name="title" translatable="yes" comments="Title of Straight-Line Depreciation dialog">Straight-Line Depreciation</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
- <signal name="response" handler="finc_response_cb" swapped="no"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox7">
<property name="visible">True</property>
@@ -1407,7 +1379,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1423,7 +1394,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1439,7 +1409,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1483,7 +1452,6 @@
<property name="title" translatable="yes" comments="Title of Sum-of-the-Years'-Digits Depreciation dialog">Sum-of-the-Years'-Digits Depreciation</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
- <signal name="response" handler="finc_response_cb" swapped="no"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox8">
<property name="visible">True</property>
@@ -1612,7 +1580,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1628,7 +1595,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1644,7 +1610,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1660,7 +1625,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1704,7 +1668,6 @@
<property name="title" translatable="yes" comments="Title of Payment Period dialog">Payment Period</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
- <signal name="response" handler="finc_response_cb" swapped="no"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox9">
<property name="visible">True</property>
@@ -1769,7 +1732,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1785,7 +1747,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1801,7 +1762,6 @@
<property name="can_focus">True</property>
<property name="invisible_char">â</property>
<property name="text">0</property>
- <signal name="activate" handler="finc_activate_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -1908,7 +1868,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the memory value button">Memory</property>
</object>
</child>
- <signal name="clicked" handler="memory_cb" swapped="no"/>
<child>
<object class="GtkHBox" id="hbox20">
<property name="visible">True</property>
@@ -1961,7 +1920,6 @@
<property name="receives_default">True</property>
<property name="use_action_appearance">False</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">6</property>
@@ -1981,7 +1939,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="top_attach">1</property>
@@ -1999,7 +1956,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="x_options">GTK_EXPAND | GTK_SHRINK | GTK_FILL</property>
@@ -2015,7 +1971,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -2033,7 +1988,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -2051,7 +2005,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -2071,7 +2024,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -2091,7 +2043,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -2109,7 +2060,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="top_attach">2</property>
@@ -2127,7 +2077,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -2148,7 +2097,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="top_attach">3</property>
@@ -2166,7 +2114,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="numeric_point_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -2186,7 +2133,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -2206,7 +2152,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -2226,7 +2171,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -2246,7 +2190,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="subtract_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -2266,7 +2209,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -2286,7 +2228,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="solve_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">4</property>
@@ -2306,7 +2247,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">4</property>
@@ -2326,7 +2266,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">5</property>
@@ -2345,7 +2284,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="clear_cb" swapped="no"/>
<child>
<object class="GtkImage" id="image2">
<property name="visible">True</property>
@@ -2370,7 +2308,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="finc_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">7</property>
@@ -2388,7 +2325,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="finc_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">8</property>
@@ -2406,7 +2342,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="finc_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">9</property>
@@ -2424,7 +2359,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="finc_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">8</property>
@@ -2444,7 +2378,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="finc_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">9</property>
@@ -2464,7 +2397,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="finc_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">8</property>
@@ -2484,7 +2416,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="finc_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">7</property>
@@ -2504,7 +2435,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="finc_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">9</property>
@@ -2524,7 +2454,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="finc_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">8</property>
@@ -2544,7 +2473,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="finc_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">7</property>
@@ -2590,7 +2518,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the exponentiation (x to the power of y) button">Exponent</property>
</object>
</child>
- <signal name="clicked" handler="button_cb" swapped="no"/>
<child>
<object class="GtkLabel" id="x_pow_y_label">
<property name="visible">True</property>
@@ -2616,7 +2543,6 @@
<property name="use_action_appearance">False</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">6</property>
diff --git a/data/buttons-programming.ui b/data/buttons-programming.ui
index ee56c80..4c0fc8e 100644
--- a/data/buttons-programming.ui
+++ b/data/buttons-programming.ui
@@ -50,7 +50,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_1">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_1">
<property name="visible">True</property>
@@ -69,7 +68,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_2">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_2">
<property name="visible">True</property>
@@ -88,7 +86,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_3">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_3">
<property name="visible">True</property>
@@ -107,7 +104,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_4">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_4">
<property name="visible">True</property>
@@ -126,7 +122,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_5">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_5">
<property name="visible">True</property>
@@ -145,7 +140,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_6">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_6">
<property name="visible">True</property>
@@ -164,7 +158,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_7">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_7">
<property name="visible">True</property>
@@ -183,7 +176,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_8">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_8">
<property name="visible">True</property>
@@ -202,7 +194,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_9">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_9">
<property name="visible">True</property>
@@ -221,7 +212,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_10">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_10">
<property name="visible">True</property>
@@ -240,7 +230,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_11">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_11">
<property name="visible">True</property>
@@ -259,7 +248,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_12">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_12">
<property name="visible">True</property>
@@ -278,7 +266,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_13">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_13">
<property name="visible">True</property>
@@ -297,7 +284,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_14">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_14">
<property name="visible">True</property>
@@ -316,7 +302,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_15">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_15">
<property name="visible">True</property>
@@ -335,7 +320,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_31">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_31">
<property name="visible">True</property>
@@ -354,7 +338,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_30">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_30">
<property name="visible">True</property>
@@ -373,7 +356,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_29">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_29">
<property name="visible">True</property>
@@ -392,7 +374,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_28">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_28">
<property name="visible">True</property>
@@ -411,7 +392,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_27">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_27">
<property name="visible">True</property>
@@ -430,7 +410,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_26">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_26">
<property name="visible">True</property>
@@ -449,7 +428,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_25">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_25">
<property name="visible">True</property>
@@ -468,7 +446,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_24">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_24">
<property name="visible">True</property>
@@ -487,7 +464,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_23">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_23">
<property name="visible">True</property>
@@ -506,7 +482,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_22">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_22">
<property name="visible">True</property>
@@ -525,7 +500,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_21">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_21">
<property name="visible">True</property>
@@ -544,7 +518,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_20">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_20">
<property name="visible">True</property>
@@ -563,7 +536,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_19">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_19">
<property name="visible">True</property>
@@ -582,7 +554,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_18">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_18">
<property name="visible">True</property>
@@ -601,7 +572,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_17">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_17">
<property name="visible">True</property>
@@ -620,7 +590,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_16">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_16">
<property name="visible">True</property>
@@ -699,7 +668,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_32">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_32">
<property name="visible">True</property>
@@ -718,7 +686,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_33">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_33">
<property name="visible">True</property>
@@ -739,7 +706,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_34">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_34">
<property name="visible">True</property>
@@ -760,7 +726,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_35">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_35">
<property name="visible">True</property>
@@ -781,7 +746,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_36">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_36">
<property name="visible">True</property>
@@ -802,7 +766,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_37">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_37">
<property name="visible">True</property>
@@ -823,7 +786,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_38">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_38">
<property name="visible">True</property>
@@ -844,7 +806,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_39">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_39">
<property name="visible">True</property>
@@ -865,7 +826,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_40">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_40">
<property name="visible">True</property>
@@ -886,7 +846,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_41">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_41">
<property name="visible">True</property>
@@ -907,7 +866,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_42">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_42">
<property name="visible">True</property>
@@ -928,7 +886,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_43">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_43">
<property name="visible">True</property>
@@ -949,7 +906,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_44">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_44">
<property name="visible">True</property>
@@ -970,7 +926,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_45">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_45">
<property name="visible">True</property>
@@ -991,7 +946,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_46">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_46">
<property name="visible">True</property>
@@ -1012,7 +966,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_47">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_47">
<property name="visible">True</property>
@@ -1033,7 +986,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_48">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_48">
<property name="visible">True</property>
@@ -1054,7 +1006,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_49">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_49">
<property name="visible">True</property>
@@ -1075,7 +1026,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_50">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_50">
<property name="visible">True</property>
@@ -1096,7 +1046,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_51">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_51">
<property name="visible">True</property>
@@ -1117,7 +1066,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_52">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_52">
<property name="visible">True</property>
@@ -1138,7 +1086,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_53">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_53">
<property name="visible">True</property>
@@ -1159,7 +1106,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_54">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_54">
<property name="visible">True</property>
@@ -1180,7 +1126,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_55">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_55">
<property name="visible">True</property>
@@ -1201,7 +1146,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_56">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_56">
<property name="visible">True</property>
@@ -1222,7 +1166,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_57">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_57">
<property name="visible">True</property>
@@ -1243,7 +1186,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_58">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_58">
<property name="visible">True</property>
@@ -1264,7 +1206,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_59">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_59">
<property name="visible">True</property>
@@ -1285,7 +1226,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_60">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_60">
<property name="visible">True</property>
@@ -1306,7 +1246,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_61">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_61">
<property name="visible">True</property>
@@ -1327,7 +1266,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_62">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_62">
<property name="visible">True</property>
@@ -1348,7 +1286,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_63">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_63">
<property name="visible">True</property>
@@ -1597,7 +1534,6 @@
<child>
<object class="GtkEventBox" id="bit_eventbox_0">
<property name="visible">True</property>
- <signal name="button_press_event" handler="bit_toggle_cb"/>
<child>
<object class="GtkLabel" id="bit_label_0">
<property name="visible">True</property>
@@ -1849,7 +1785,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">7</property>
@@ -1867,7 +1802,6 @@
<property name="can_focus">True</property>
<property name="receives_default">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">7</property>
@@ -1891,7 +1825,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the factorize button">Factorize</property>
</object>
</child>
- <signal name="clicked" handler="factorize_cb"/>
</object>
<packing>
<property name="left_attach">7</property>
@@ -1910,7 +1843,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">8</property>
@@ -1933,7 +1865,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the absolute value button">Absolute Value</property>
</object>
</child>
- <signal name="clicked" handler="button_cb"/>
<child>
<object class="GtkLabel" id="label1">
<property name="visible">True</property>
@@ -1959,7 +1890,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">9</property>
@@ -1978,7 +1908,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">9</property>
@@ -1997,7 +1926,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">4</property>
@@ -2017,7 +1945,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="top_attach">4</property>
@@ -2034,7 +1961,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">4</property>
@@ -2053,7 +1979,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="subtract_cb"/>
</object>
<packing>
<property name="left_attach">4</property>
@@ -2072,7 +1997,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">4</property>
@@ -2091,7 +2015,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="solve_cb"/>
</object>
<packing>
<property name="left_attach">6</property>
@@ -2109,7 +2032,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="clear_cb"/>
<child>
<object class="GtkImage" id="image1">
<property name="visible">True</property>
@@ -2134,7 +2056,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -2153,7 +2074,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -2172,7 +2092,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -2191,7 +2110,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="top_attach">3</property>
@@ -2208,7 +2126,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -2227,7 +2144,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -2246,7 +2162,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -2265,7 +2180,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="top_attach">2</property>
@@ -2282,7 +2196,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -2301,7 +2214,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -2320,7 +2232,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -2339,7 +2250,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="top_attach">1</property>
@@ -2356,7 +2266,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">1</property>
@@ -2375,7 +2284,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -2394,7 +2302,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="digit_cb"/>
</object>
<packing>
<property name="left_attach">3</property>
@@ -2416,7 +2323,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the subscript mode button">Subscript</property>
</object>
</child>
- <signal name="clicked" handler="set_subscript_cb"/>
<child>
<object class="GtkLabel" id="label4">
<property name="visible">True</property>
@@ -2441,7 +2347,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the superscript mode button">Superscript</property>
</object>
</child>
- <signal name="clicked" handler="set_superscript_cb"/>
<child>
<object class="GtkLabel" id="label3">
<property name="visible">True</property>
@@ -2465,7 +2370,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="numeric_point_cb" swapped="no"/>
</object>
<packing>
<property name="left_attach">2</property>
@@ -2485,7 +2389,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the store value button">Store</property>
</object>
</child>
- <signal name="clicked" handler="memory_cb"/>
<child>
<object class="GtkHBox" id="hbox20">
<property name="visible">True</property>
@@ -2527,7 +2430,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">5</property>
@@ -2546,7 +2448,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">5</property>
@@ -2565,7 +2466,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">5</property>
@@ -2584,7 +2484,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">5</property>
@@ -2603,7 +2502,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">4</property>
@@ -2620,7 +2518,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">5</property>
@@ -2640,7 +2537,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the shift left button">Shift Left</property>
</object>
</child>
- <signal name="clicked" handler="shift_left_cb"/>
<child>
<object class="GtkHBox" id="hbox11">
<property name="visible">True</property>
@@ -2686,7 +2582,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the shift right button">Shift Right</property>
</object>
</child>
- <signal name="clicked" handler="shift_right_cb"/>
<child>
<object class="GtkHBox" id="hbox10">
<property name="visible">True</property>
@@ -2733,7 +2628,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the insert character button">Insert Character</property>
</object>
</child>
- <signal name="clicked" handler="insert_character_code_cb"/>
</object>
<packing>
<property name="left_attach">8</property>
@@ -2750,7 +2644,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">6</property>
@@ -2772,7 +2665,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the factorial button">Factorial</property>
</object>
</child>
- <signal name="clicked" handler="button_cb"/>
<child>
<object class="GtkLabel" id="label14">
<property name="visible">True</property>
@@ -2801,7 +2693,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the inverse button">Inverse</property>
</object>
</child>
- <signal name="clicked" handler="button_cb"/>
<child>
<object class="GtkLabel" id="label20">
<property name="visible">True</property>
@@ -2831,7 +2722,6 @@
<property name="AtkObject::accessible-name" translatable="yes" comments="Accessible name for the exponentiation (x to the power of y) button">Exponent</property>
</object>
</child>
- <signal name="clicked" handler="button_cb"/>
<child>
<object class="GtkLabel" id="x_pow_y_label">
<property name="visible">True</property>
@@ -2857,7 +2747,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">6</property>
@@ -2876,7 +2765,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">7</property>
@@ -2895,7 +2783,6 @@
<property name="receives_default">True</property>
<property name="use_underline">True</property>
<property name="focus_on_click">False</property>
- <signal name="clicked" handler="button_cb"/>
</object>
<packing>
<property name="left_attach">8</property>
@@ -2922,8 +2809,6 @@
<property name="title" translatable="yes" comments="Title of insert character code dialog">Insert Character Code</property>
<property name="resizable">False</property>
<property name="type_hint">dialog</property>
- <signal name="response" handler="character_code_dialog_response_cb"/>
- <signal name="delete_event" handler="character_code_dialog_delete_cb"/>
<child internal-child="vbox">
<object class="GtkVBox" id="dialog-vbox3">
<property name="visible">True</property>
@@ -2950,7 +2835,6 @@
<property name="can_focus">True</property>
<property name="max_length">1</property>
<property name="invisible_char">â</property>
- <signal name="activate" handler="character_code_dialog_activate_cb"/>
</object>
<packing>
<property name="expand">False</property>
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 0401271..b99f5ad 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -5,28 +5,21 @@
[type: gettext/glade]data/buttons-basic.ui
[type: gettext/glade]data/buttons-financial.ui
[type: gettext/glade]data/buttons-programming.ui
-[type: gettext/glade]data/preferences.ui
data/gcalctool.desktop.in
data/org.gnome.gcalctool.gschema.xml.in
-src/currency.c
-src/currency-manager.c
-src/financial.c
-src/gcalccmd.c
-src/gcalctool.c
-src/math-buttons.c
-src/math-converter.c
-src/math-display.c
-src/math-equation.c
-src/math-preferences.c
-src/math-variable-popup.c
-src/math-variables.c
-src/math-window.c
-src/mp-binary.c
-src/mp.c
-src/mp-convert.c
-src/mp-equation.c
-src/mp-serializer.c
-src/mp-trigonometric.c
-src/unit.c
-src/unit-category.c
-src/unit-manager.c
+src/currency.vala
+src/equation.vala
+src/financial.vala
+src/gcalccmd.vala
+src/gcalctool.vala
+src/math-buttons.vala
+src/math-converter.vala
+src/math-display.vala
+src/math-equation.vala
+src/math-preferences.vala
+src/math-variable-popup.vala
+src/math-variables.vala
+src/math-window.vala
+src/number.vala
+src/serializer.vala
+src/unit.vala
diff --git a/src/Makefile.am b/src/Makefile.am
index 188908c..d46be49 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,7 +1,7 @@
bin_PROGRAMS = gcalctool gcalccmd
-noinst_PROGRAMS = test-mp test-mp-equation
+noinst_PROGRAMS = test-number test-equation
-TESTS = test-mp test-mp-equation
+TESTS = test-number test-equation
INCLUDES = \
-DUI_DIR=\""$(datadir)/gcalctool"\" \
@@ -12,157 +12,92 @@ INCLUDES = \
$(GCALCTOOL_CFLAGS)
gcalctool_SOURCES = \
- gcalctool.c \
- currency.c \
- currency.h \
- currency-manager.c \
- currency-manager.h \
- math-buttons.c \
- math-buttons.h \
- math-converter.c \
- math-converter.h \
- math-display.c \
- math-display.h \
- math-equation.c \
- math-equation.h \
- math-preferences.c \
- math-preferences.h \
- math-variables.c \
- math-variables.h \
- math-variable-popup.c \
- math-variable-popup.h \
- math-window.c \
- math-window.h \
- mp.c \
- mp.h \
- mp-binary.c \
- mp-convert.c \
- mp-enums.c \
- mp-enums.h \
- mp-equation.c \
- mp-equation.h \
- mp-private.h \
- mp-serializer.c \
- mp-serializer.h \
- mp-trigonometric.c \
- financial.c \
- financial.h \
- unit.c \
- unit.h \
- unit-category.c \
- unit-category.h \
- unit-manager.c \
- unit-manager.h \
- prelexer.c \
- prelexer.h \
- lexer.c \
- lexer.h \
- parserfunc.c \
- parserfunc.h \
- parser.c \
- parser.h
+ config.vapi \
+ gcalctool.vala \
+ currency.vala \
+ equation.vala \
+ equation-lexer.vala \
+ equation-parser.vala \
+ financial.vala \
+ math-buttons.vala \
+ math-converter.vala \
+ math-display.vala \
+ math-equation.vala \
+ math-preferences.vala \
+ math-variables.vala \
+ math-variable-popup.vala \
+ math-window.vala \
+ number.vala \
+ serializer.vala \
+ unit.vala
+
+gcalctool_VALAFLAGS = \
+ --target-glib 2.32 \
+ --pkg posix \
+ --pkg gtk+-3.0 \
+ --pkg libxml-2.0
gcalctool_LDADD = \
$(GCALCTOOL_LIBS)
gcalccmd_SOURCES = \
- gcalccmd.c \
- currency.c \
- currency.h \
- currency-manager.c \
- currency-manager.h \
- mp.c \
- mp-binary.c \
- mp-convert.c \
- mp-enums.c \
- mp-enums.h \
- mp-equation.c \
- mp-serializer.c \
- mp-serializer.h\
- mp-trigonometric.c \
- unit.c \
- unit.h \
- unit-category.c \
- unit-category.h \
- unit-manager.c \
- unit-manager.h \
- prelexer.c \
- prelexer.h \
- lexer.c \
- lexer.h \
- parserfunc.c \
- parserfunc.h \
- parser.c \
- parser.h
+ config.vapi \
+ gcalccmd.vala \
+ currency.vala \
+ equation.vala \
+ equation-lexer.vala \
+ equation-parser.vala \
+ number.vala \
+ serializer.vala \
+ unit.vala
+
+gcalccmd_VALAFLAGS = \
+ --target-glib 2.32 \
+ --pkg posix \
+ --pkg gio-2.0 \
+ --pkg libxml-2.0
gcalccmd_LDADD = \
$(GCALCCMD_LIBS) \
-lm
-test_mp_SOURCES = \
- test-mp.c \
- mp.c \
- mp-binary.c \
- mp-convert.c \
- mp-enums.c \
- mp-enums.h \
- mp-serializer.c \
- mp-serializer.h \
- mp-trigonometric.c
-
-test_mp_LDADD = \
+test_number_SOURCES = \
+ config.vapi \
+ test-number.vala \
+ number.vala \
+ serializer.vala
+
+tesp_mp_VALAFLAGS = \
+ --target-glib 2.32 \
+ --pkg posix \
+ --pkg gio-2.0 \
+ --pkg libxml-2.0
+
+test_number_LDADD = \
$(GCALCCMD_LIBS) \
-lm
-test_mp_equation_SOURCES = \
- test-mp-equation.c \
- currency.c \
- currency.h \
- currency-manager.c \
- currency-manager.h \
- mp.c \
- mp-convert.c \
- mp-binary.c \
- mp-enums.c \
- mp-enums.h \
- mp-equation.c \
- mp-serializer.c \
- mp-serializer.h \
- mp-trigonometric.c \
- unit.c \
- unit.h \
- unit-category.c \
- unit-category.h \
- unit-manager.c \
- unit-manager.h \
- prelexer.c \
- prelexer.h \
- lexer.c \
- lexer.h \
- parserfunc.c \
- parserfunc.h \
- parser.c \
- parser.h
-
-test_mp_equation_LDADD = \
+test_equation_SOURCES = \
+ config.vapi \
+ test-equation.vala \
+ currency.vala \
+ equation.vala \
+ equation-lexer.vala \
+ equation-parser.vala \
+ number.vala \
+ serializer.vala \
+ unit.vala
+
+test_equation_VALAFLAGS = \
+ --target-glib 2.32 \
+ --pkg posix \
+ --pkg gio-2.0 \
+ --pkg libxml-2.0
+
+test_equation_LDADD = \
$(GCALCCMD_LIBS) \
-lm
-CLEANFILES = \
- mp-enums.c \
- mp-enums.h
-
-# Generate enum types
-mp-enums.h: mp-enums.h.template mp-serializer.h
- $(AM_V_GEN)$(GLIB_MKENUMS) --template $(srcdir)/mp-enums.h.template $(srcdir)/mp-serializer.h > mp-enums.h
-
-mp-enums.c: mp-enums.c.template mp-enums.h mp-serializer.h
- $(AM_V_GEN)$(GLIB_MKENUMS) --template $(srcdir)/mp-enums.c.template $(srcdir)/mp-serializer.h > mp-enums.c
-
-# Fix dependencies
-math-serializer.c: mp-enums.h
-math-equation.c: mp-enums.h
-
# Install a symlink between gcalctool and gnome-calculator
install-exec-hook:
test -e "$(DESTDIR)$(bindir)/gnome-calculator" \
@@ -173,10 +108,6 @@ uninstall-local:
test -h "$(DESTDIR)$(bindir)/gnome-calculator" \
&& rm -f "$(DESTDIR)$(bindir)/gnome-calculator"
-EXTRA_DIST = \
- mp-enums.c.template \
- mp-enums.h.template
-
DISTCLEANFILES = \
Makefile.in
diff --git a/src/config.vapi b/src/config.vapi
new file mode 100644
index 0000000..e9f4270
--- /dev/null
+++ b/src/config.vapi
@@ -0,0 +1,6 @@
+public const string VERSION;
+public const string GETTEXT_PACKAGE;
+public const string LOCALE_DIR;
+public const string UI_DIR;
+
+
diff --git a/src/currency.vala b/src/currency.vala
new file mode 100644
index 0000000..eb38232
--- /dev/null
+++ b/src/currency.vala
@@ -0,0 +1,499 @@
+/*
+ * Copyright (C) 2008-2012 Robert Ancell.
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+static bool downloading_imf_rates = false;
+static bool downloading_ecb_rates = false;
+static bool loaded_rates = false;
+private static CurrencyManager? default_currency_manager = null;
+
+public class CurrencyManager : Object
+{
+ private List<Currency> currencies;
+ public signal void updated ();
+
+ public static CurrencyManager get_default ()
+ {
+ if (default_currency_manager != null)
+ return default_currency_manager;
+
+ default_currency_manager = new CurrencyManager ();
+
+ default_currency_manager.currencies.append (new Currency ("AED", _("UAE Dirham"), "Ø.Ø"));
+ default_currency_manager.currencies.append (new Currency ("AUD", _("Australian Dollar"), "$"));
+ default_currency_manager.currencies.append (new Currency ("BGN", _("Bulgarian Lev"), "ÐÐ"));
+ default_currency_manager.currencies.append (new Currency ("BHD", _("Bahraini Dinar"), ".Ø.Ø"));
+ default_currency_manager.currencies.append (new Currency ("BND", _("Brunei Dollar"), "$"));
+ default_currency_manager.currencies.append (new Currency ("BRL", _("Brazilian Real"), "R$"));
+ default_currency_manager.currencies.append (new Currency ("BWP", _("Botswana Pula"), "P"));
+ default_currency_manager.currencies.append (new Currency ("CAD", _("Canadian Dollar"), "$"));
+ default_currency_manager.currencies.append (new Currency ("CFA", _("CFA Franc"), "Fr"));
+ default_currency_manager.currencies.append (new Currency ("CHF", _("Swiss Franc"), "Fr"));
+ default_currency_manager.currencies.append (new Currency ("CLP", _("Chilean Peso"), "$"));
+ default_currency_manager.currencies.append (new Currency ("CNY", _("Chinese Yuan"), "å"));
+ default_currency_manager.currencies.append (new Currency ("COP", _("Colombian Peso"), "$"));
+ default_currency_manager.currencies.append (new Currency ("CZK", _("Czech Koruna"), "KÄ"));
+ default_currency_manager.currencies.append (new Currency ("DKK", _("Danish Krone"), "kr"));
+ default_currency_manager.currencies.append (new Currency ("DZD", _("Algerian Dinar"), "Ø.Ø"));
+ default_currency_manager.currencies.append (new Currency ("EEK", _("Estonian Kroon"), "KR"));
+ default_currency_manager.currencies.append (new Currency ("EUR", _("Euro"), "â"));
+ default_currency_manager.currencies.append (new Currency ("GBP", _("Pound Sterling"), "Â"));
+ default_currency_manager.currencies.append (new Currency ("HKD", _("Hong Kong Dollar"), "$"));
+ default_currency_manager.currencies.append (new Currency ("HRK", _("Croatian Kuna"), "kn"));
+ default_currency_manager.currencies.append (new Currency ("HUF", _("Hungarian Forint"), "Ft"));
+ default_currency_manager.currencies.append (new Currency ("IDR", _("Indonesian Rupiah"), "Rp"));
+ default_currency_manager.currencies.append (new Currency ("ILS", _("Israeli New Shekel"), "â"));
+ default_currency_manager.currencies.append (new Currency ("INR", _("Indian Rupee"), "â"));
+ default_currency_manager.currencies.append (new Currency ("IRR", _("Iranian Rial"), "ï"));
+ default_currency_manager.currencies.append (new Currency ("ISK", _("Icelandic Krona"), "kr"));
+ default_currency_manager.currencies.append (new Currency ("JPY", _("Japanese Yen"), "Â"));
+ default_currency_manager.currencies.append (new Currency ("KRW", _("South Korean Won"), "â"));
+ default_currency_manager.currencies.append (new Currency ("KWD", _("Kuwaiti Dinar"), "Ù.Ø"));
+ default_currency_manager.currencies.append (new Currency ("KZT", _("Kazakhstani Tenge"), "â"));
+ default_currency_manager.currencies.append (new Currency ("LKR", _("Sri Lankan Rupee"), "Rs"));
+ default_currency_manager.currencies.append (new Currency ("LTL", _("Lithuanian Litas"), "Lt"));
+ default_currency_manager.currencies.append (new Currency ("LVL", _("Latvian Lats"), "Ls"));
+ default_currency_manager.currencies.append (new Currency ("LYD", _("Libyan Dinar"), "Ø.Ù"));
+ default_currency_manager.currencies.append (new Currency ("MUR", _("Mauritian Rupee"), "Rs"));
+ default_currency_manager.currencies.append (new Currency ("MXN", _("Mexican Peso"), "$"));
+ default_currency_manager.currencies.append (new Currency ("MYR", _("Malaysian Ringgit"), "RM"));
+ default_currency_manager.currencies.append (new Currency ("NOK", _("Norwegian Krone"), "kr"));
+ default_currency_manager.currencies.append (new Currency ("NPR", _("Nepalese Rupee"), "Rs"));
+ default_currency_manager.currencies.append (new Currency ("NZD", _("New Zealand Dollar"), "$"));
+ default_currency_manager.currencies.append (new Currency ("OMR", _("Omani Rial"), "Ø.Ø."));
+ default_currency_manager.currencies.append (new Currency ("PEN", _("Peruvian Nuevo Sol"), "S/."));
+ default_currency_manager.currencies.append (new Currency ("PHP", _("Philippine Peso"), "â"));
+ default_currency_manager.currencies.append (new Currency ("PKR", _("Pakistani Rupee"), "Rs"));
+ default_currency_manager.currencies.append (new Currency ("PLN", _("Polish Zloty"), "zÅ"));
+ default_currency_manager.currencies.append (new Currency ("QAR", _("Qatari Riyal"), "Ù.Ø"));
+ default_currency_manager.currencies.append (new Currency ("RON", _("New Romanian Leu"), "L"));
+ default_currency_manager.currencies.append (new Currency ("RUB", _("Russian Rouble"), "ÑÑÐ."));
+ default_currency_manager.currencies.append (new Currency ("SAR", _("Saudi Riyal"), "Ø.Ø"));
+ default_currency_manager.currencies.append (new Currency ("SEK", _("Swedish Krona"), "kr"));
+ default_currency_manager.currencies.append (new Currency ("SGD", _("Singapore Dollar"), "$"));
+ default_currency_manager.currencies.append (new Currency ("THB", _("Thai Baht"), "à"));
+ default_currency_manager.currencies.append (new Currency ("TND", _("Tunisian Dinar"), "Ø.Ø"));
+ default_currency_manager.currencies.append (new Currency ("TRY", _("New Turkish Lira"), "TL"));
+ default_currency_manager.currencies.append (new Currency ("TTD", _("T&T Dollar (TTD)"), "$"));
+ default_currency_manager.currencies.append (new Currency ("USD", _("US Dollar"), "$"));
+ default_currency_manager.currencies.append (new Currency ("UYU", _("Uruguayan Peso"), "$"));
+ default_currency_manager.currencies.append (new Currency ("VEF", _("Venezuelan BolÃvar"), "Bs F"));
+ default_currency_manager.currencies.append (new Currency ("ZAR", _("South African Rand"), "R"));
+
+ return default_currency_manager;
+ }
+
+ public List<Currency> get_currencies ()
+ {
+ var r = new List<Currency> ();
+ foreach (var c in currencies)
+ r.append (c);
+ return r;
+ }
+
+ public Currency? get_currency (string name)
+ {
+ foreach (var c in currencies)
+ {
+ if (name == c.name)
+ {
+ var value = c.get_value ();
+ if (value == null || value.is_negative () || value.is_zero ())
+ return null;
+ else
+ return c;
+ }
+ }
+
+ return null;
+ }
+
+ private string get_imf_rate_filepath ()
+ {
+ return Path.build_filename (Environment.get_user_cache_dir (), "gcalctool", "rms_five.xls");
+ }
+
+ private string get_ecb_rate_filepath ()
+ {
+ return Path.build_filename (Environment.get_user_cache_dir (), "gcalctool", "eurofxref-daily.xml");
+ }
+
+ private Currency add_currency (string short_name)
+ {
+ foreach (var c in currencies)
+ if (c.name == short_name)
+ return c;
+
+ warning ("Currency %s is not in the currency table", short_name);
+ var c = new Currency (short_name, short_name, short_name);
+ currencies.append (c);
+
+ return c;
+ }
+
+ /* A file needs to be redownloaded if it doesn't exist, or is too old.
+ * When an error occur, it probably won't hurt to try to download again.
+ */
+ private bool file_needs_update (string filename, double max_age)
+ {
+ if (!FileUtils.test (filename, FileTest.IS_REGULAR))
+ return true;
+
+ var buf = Posix.Stat ();
+ if (Posix.stat (filename, out buf) == -1)
+ return true;
+
+ var modify_time = buf.st_mtime;
+ var now = time_t ();
+ if (now - modify_time > max_age)
+ return true;
+
+ return false;
+ }
+
+ private void load_imf_rates ()
+ {
+ var name_map = new HashTable <string, string> (str_hash, str_equal);
+ name_map.insert ("Euro", "EUR");
+ name_map.insert ("Japanese Yen", "JPY");
+ name_map.insert ("U.K. Pound Sterling", "GBP");
+ name_map.insert ("U.S. Dollar", "USD");
+ name_map.insert ("Algerian Dinar", "DZD");
+ name_map.insert ("Australian Dollar", "AUD");
+ name_map.insert ("Bahrain Dinar", "BHD");
+ name_map.insert ("Botswana Pula", "BWP");
+ name_map.insert ("Brazilian Real", "BRL");
+ name_map.insert ("Brunei Dollar", "BND");
+ name_map.insert ("Canadian Dollar", "CAD");
+ name_map.insert ("Chilean Peso", "CLP");
+ name_map.insert ("Chinese Yuan", "CNY");
+ name_map.insert ("Colombian Peso", "COP");
+ name_map.insert ("Czech Koruna", "CZK");
+ name_map.insert ("Danish Krone", "DKK");
+ name_map.insert ("Hungarian Forint", "HUF");
+ name_map.insert ("Icelandic Krona", "ISK");
+ name_map.insert ("Indian Rupee", "INR");
+ name_map.insert ("Indonesian Rupiah", "IDR");
+ name_map.insert ("Iranian Rial", "IRR");
+ name_map.insert ("Israeli New Sheqel", "ILS");
+ name_map.insert ("Kazakhstani Tenge", "KZT");
+ name_map.insert ("Korean Won", "KRW");
+ name_map.insert ("Kuwaiti Dinar", "KWD");
+ name_map.insert ("Libyan Dinar", "LYD");
+ name_map.insert ("Malaysian Ringgit", "MYR");
+ name_map.insert ("Mauritian Rupee", "MUR");
+ name_map.insert ("Mexican Peso", "MXN");
+ name_map.insert ("Nepalese Rupee", "NPR");
+ name_map.insert ("New Zealand Dollar", "NZD");
+ name_map.insert ("Norwegian Krone", "NOK");
+ name_map.insert ("Rial Omani", "OMR");
+ name_map.insert ("Pakistani Rupee", "PKR");
+ name_map.insert ("Nuevo Sol", "PEN");
+ name_map.insert ("Philippine Peso", "PHP");
+ name_map.insert ("Polish Zloty", "PLN");
+ name_map.insert ("Qatar Riyal", "QAR");
+ name_map.insert ("Russian Ruble", "RUB");
+ name_map.insert ("Saudi Arabian Riyal", "SAR");
+ name_map.insert ("Singapore Dollar", "SGD");
+ name_map.insert ("South African Rand", "ZAR");
+ name_map.insert ("Sri Lanka Rupee", "LKR");
+ name_map.insert ("Swedish Krona", "SEK");
+ name_map.insert ("Swiss Franc", "CHF");
+ name_map.insert ("Thai Baht", "THB");
+ name_map.insert ("Trinidad And Tobago Dollar", "TTD");
+ name_map.insert ("Tunisian Dinar", "TND");
+ name_map.insert ("U.A.E. Dirham", "AED");
+ name_map.insert ("Peso Uruguayo", "UYU");
+ name_map.insert ("Bolivar Fuerte", "VEF");
+
+ var filename = get_imf_rate_filepath ();
+ string data;
+ try
+ {
+ FileUtils.get_contents (filename, out data);
+ }
+ catch (Error e)
+ {
+ warning ("Failed to read exchange rates: %s", e.message);
+ return;
+ }
+
+ var lines = data.split ("\n", 0);
+
+ var in_data = false;
+ foreach (var line in lines)
+ {
+ line = line.chug ();
+
+ /* Start after first blank line, stop on next */
+ if (line == "")
+ {
+ if (!in_data)
+ {
+ in_data = true;
+ continue;
+ }
+ else
+ break;
+ }
+ if (!in_data)
+ continue;
+
+ var tokens = line.split ("\t", 0);
+ if (tokens[0] != "Currency")
+ {
+ int value_index;
+ for (value_index = 1; value_index < tokens.length; value_index++)
+ {
+ var value = tokens[value_index].chug ();
+ if (value != "")
+ break;
+ }
+
+ if (value_index < tokens.length)
+ {
+ var symbol = name_map.lookup (tokens[0]);
+ if (symbol != null)
+ {
+ var c = get_currency (symbol);
+ if (c == null)
+ {
+ debug ("Using IMF rate of %s for %s", tokens[value_index], symbol);
+ c = add_currency (symbol);
+ }
+ var value = mp_set_from_string (tokens[value_index]);
+ value = value.reciprocal ();
+ c.set_value (value);
+ }
+ else
+ warning ("Unknown currency '%s'", tokens[0]);
+ }
+ }
+ }
+ }
+
+ private void set_ecb_rate (Xml.Node node, Currency eur_rate)
+ {
+ string? name = null, value = null;
+
+ for (var attribute = node.properties; attribute != null; attribute = attribute->next)
+ {
+ var n = (Xml.Node*) attribute;
+ if (attribute->name == "currency")
+ name = n->get_content ();
+ else if (attribute->name == "rate")
+ value = n->get_content ();
+ }
+
+ /* Use data if value and no rate currently defined */
+ if (name != null && value != null && get_currency (name) == null)
+ {
+ debug ("Using ECB rate of %s for %s", value, name);
+ var c = add_currency (name);
+ var r = mp_set_from_string (value);
+ var v = eur_rate.get_value ();
+ v = v.multiply (r);
+ c.set_value (v);
+ }
+ }
+
+ private void set_ecb_fixed_rate (string name, string value, Currency eur_rate)
+ {
+ debug ("Using ECB fixed rate of %s for %s", value, name);
+ var c = add_currency (name);
+ var r = mp_set_from_string (value);
+ var v = eur_rate.get_value ();
+ v = v.divide (r);
+ c.set_value (v);
+ }
+
+ private void load_ecb_rates ()
+ {
+ /* Scale rates to the EUR value */
+ var eur_rate = get_currency ("EUR");
+ if (eur_rate == null)
+ {
+ warning ("Cannot use ECB rates as don't have EUR rate");
+ return;
+ }
+
+ /* Set some fixed rates */
+ set_ecb_fixed_rate ("EEK", "0.06391", eur_rate);
+ set_ecb_fixed_rate ("CFA", "0.152449", eur_rate);
+
+ Xml.Parser.init ();
+ var filename = get_ecb_rate_filepath ();
+ var document = Xml.Parser.read_file (filename);
+ if (document == null)
+ {
+ warning ("Couldn't parse ECB rate file %s", filename);
+ return;
+ }
+
+ var xpath_ctx = new Xml.XPath.Context (document);
+ if (xpath_ctx == null)
+ {
+ warning ("Couldn't create XPath context");
+ return;
+ }
+
+ xpath_ctx.register_ns ("xref", "http://www.ecb.int/vocabulary/2002-08-01/eurofxref");
+ var xpath_obj = xpath_ctx.eval_expression ("//xref:Cube[ currency][@rate]");
+ if (xpath_obj == null)
+ {
+ warning ("Couldn't create XPath object");
+ return;
+ }
+ var len = (xpath_obj->nodesetval != null) ? xpath_obj->nodesetval->length () : 0;
+ for (var i = 0; i < len; i++)
+ {
+ var node = xpath_obj->nodesetval->item (i);
+
+ if (node->type == Xml.ElementType.ELEMENT_NODE)
+ set_ecb_rate (node, eur_rate);
+
+ /* Avoid accessing removed elements */
+ if (node->type != Xml.ElementType.NAMESPACE_DECL)
+ node = null;
+ }
+
+ Xml.Parser.cleanup ();
+ }
+
+ private bool load_rates ()
+ {
+ /* Already loaded */
+ if (loaded_rates)
+ return true;
+
+ /* In process */
+ if (downloading_imf_rates || downloading_ecb_rates)
+ return false;
+
+ /* Use the IMF provided values and top up with currencies tracked by the ECB and not the IMF */
+ load_imf_rates ();
+ load_ecb_rates ();
+
+ /* Check if we couldn't find out a currency */
+ foreach (var c in currencies)
+ if (c.get_value ().is_zero ())
+ warning ("Currency %s is not provided by IMF or ECB", c.name);
+
+ debug ("Rates loaded");
+ loaded_rates = true;
+
+ updated ();
+
+ return true;
+ }
+
+ public Number? get_value (string currency)
+ {
+ /* Update rates if necessary */
+ var path = get_imf_rate_filepath ();
+ if (!downloading_imf_rates && file_needs_update (path, 60 * 60 * 24 * 7))
+ {
+ downloading_imf_rates = true;
+ debug ("Downloading rates from the IMF...");
+ download_file ("http://www.imf.org/external/np/fin/data/rms_five.aspx?tsvflag=Y", path, download_imf_cb);
+ }
+ path = get_ecb_rate_filepath ();
+ if (!downloading_ecb_rates && file_needs_update (path, 60 * 60 * 24 * 7))
+ {
+ downloading_ecb_rates = true;
+ debug ("Downloading rates from the ECB...");
+ download_file ("http://www.ecb.europa.eu/stats/eurofxref/eurofxref-daily.xml", path, download_ecb_cb);
+ }
+
+ if (!load_rates ())
+ return null;
+
+ var c = get_currency (currency);
+ if (c != null)
+ return c.get_value ();
+ else
+ return null;
+ }
+
+ private void download_file (string uri, string filename, AsyncReadyCallback callback)
+ {
+ var directory = Path.get_dirname (filename);
+ DirUtils.create_with_parents (directory, 0755);
+
+ var source = File.new_for_uri (uri);
+ var dest = File.new_for_path (filename);
+
+ source.copy_async.begin (dest, FileCopyFlags.OVERWRITE, GLib.Priority.DEFAULT, null, null, callback);
+ }
+
+ private void download_imf_cb (Object? object, AsyncResult result)
+ {
+ var f = object as File;
+
+ try
+ {
+ f.copy_async.end (result);
+ debug ("IMF rates updated");
+ }
+ catch (Error e)
+ {
+ warning ("Couldn't download IMF currency rate file: %s", e.message);
+ }
+
+ downloading_imf_rates = false;
+ load_rates ();
+ }
+
+ private void download_ecb_cb (Object? object, AsyncResult result)
+ {
+ var f = object as File;
+ try
+ {
+ f.copy_async.end (result);
+ debug ("ECB rates updated");
+ }
+ catch (Error e)
+ {
+ warning ("Couldn't download ECB currency rate file: %s", e.message);
+ }
+ downloading_ecb_rates = false;
+ load_rates ();
+ }
+}
+
+public class Currency : Object
+{
+ private Number value;
+
+ private string _name;
+ public string name { owned get { return _name; } }
+
+ private string _display_name;
+ public string display_name { owned get { return _display_name; } }
+
+ private string _symbol;
+ public string symbol { owned get { return _symbol; } }
+
+ public Currency (string name, string display_name, string symbol)
+ {
+ _name = name;
+ _display_name = display_name;
+ _symbol = symbol;
+ }
+
+ public void set_value (Number value)
+ {
+ this.value = value;
+ }
+
+ public Number get_value ()
+ {
+ return value;
+ }
+}
diff --git a/src/equation-lexer.vala b/src/equation-lexer.vala
new file mode 100644
index 0000000..b3d9fc8
--- /dev/null
+++ b/src/equation-lexer.vala
@@ -0,0 +1,681 @@
+/*
+ * Copyright (C) 2012 Arth Patel
+ * Copyright (C) 2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+/* Enum for tokens generated by pre-lexer and lexer. */
+public enum LexerTokenType
+{
+ UNKNOWN, /* Unknown */
+
+ /* These are all Pre-Lexer tokens, returned by pre-lexer */
+ PL_DECIMAL, /* Decimal separator */
+ PL_DIGIT, /* Decimal digit */
+ PL_HEX, /* A-F of Hex digits */
+ PL_SUPER_DIGIT, /* Super digits */
+ PL_SUPER_MINUS, /* Super minus */
+ PL_SUB_DIGIT, /* Sub digits */
+ PL_FRACTION, /* Fractions */
+ PL_DEGREE, /* Degree */
+ PL_MINUTE, /* Minutes */
+ PL_SECOND, /* Seconds */
+ PL_LETTER, /* Alphabets */
+ PL_EOS, /* End of stream */
+ PL_SKIP, /* Skip this symbol (whitespace or newline). */
+
+ /* These are all tokens, returned by Lexer. */
+ ADD, /* Plus */
+ SUBTRACT, /* Minus */
+ MULTIPLY, /* Multiply */
+ DIVIDE, /* Divide */
+ MOD, /* Modulus */
+ L_FLOOR, /* Floor ( Left ) */
+ R_FLOOR, /* Floor ( Right ) */
+ L_CEILING, /* Ceiling ( Left ) */
+ R_CEILING, /* Ceiling ( Right ) */
+ ROOT, /* Square root */
+ ROOT_3, /* Cube root */
+ ROOT_4, /* Fourth root */
+ NOT, /* Bitwise NOT */
+ AND, /* Bitwise AND */
+ OR, /* Bitwise OR */
+ XOR, /* Bitwise XOR */
+ IN, /* IN ( for converter ) */
+ NUMBER, /* Number */
+ SUP_NUMBER, /* Super Number */
+ NSUP_NUMBER, /* Negative Super Number */
+ SUB_NUMBER, /* Sub Number */
+ FUNCTION, /* Function */
+ VARIABLE, /* Variable name */
+ ASSIGN, /* = */
+ L_R_BRACKET, /* ( */
+ R_R_BRACKET, /* ) */
+ L_S_BRACKET, /* [ */
+ R_S_BRACKET, /* ] */
+ L_C_BRACKET, /* { */
+ R_C_BRACKET, /* } */
+ ABS, /* | */
+ POWER, /* ^ */
+ FACTORIAL, /* ! */
+ PERCENTAGE /* % */
+}
+
+// FIXME: Merge into lexer
+public class PreLexer
+{
+ public string stream; /* String being scanned */
+ public int index; /* Current character index */
+ public int mark_index; /* Location, last marked. Useful for getting substrings as part of highlighting */
+ private bool eos = false;
+
+ public PreLexer (string input)
+ {
+ stream = input;
+ index = 0;
+ mark_index = 0;
+ }
+
+ /* Roll back last scanned unichar. */
+ public void roll_back ()
+ {
+ if (eos)
+ {
+ eos = false;
+ return;
+ }
+ unichar c;
+ stream.get_prev_char (ref index, out c);
+ }
+
+ /* Set marker index. To be used for highlighting and error reporting. */
+ public void set_marker ()
+ {
+ mark_index = index;
+ }
+
+ /* Get marked substring. To be used for error reporting. */
+ public string get_marked_substring ()
+ {
+ return stream.substring (mark_index, index - mark_index);
+ }
+
+ /* Pre-Lexer tokanizer. To be called only by Lexer. */
+ public LexerTokenType get_next_token ()
+ {
+ unichar c;
+ if (!stream.get_next_char (ref index, out c))
+ {
+ // We have to flag if we ran out of chars, as roll_back from PL_EOS should have no effect
+ eos = true;
+ return LexerTokenType.PL_EOS;
+ }
+ eos = false;
+
+ if (c == ',' || c == '.')
+ return LexerTokenType.PL_DECIMAL;
+
+ if (c.isdigit ())
+ return LexerTokenType.PL_DIGIT;
+
+ if ((c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'))
+ return LexerTokenType.PL_HEX;
+
+ if (c == 'â' || c == 'Â' || c == 'Â' || c == 'Â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â')
+ return LexerTokenType.PL_SUPER_DIGIT;
+
+ if (c == 'â')
+ return LexerTokenType.PL_SUPER_MINUS;
+
+ if (c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â')
+ return LexerTokenType.PL_SUB_DIGIT;
+
+ if (c == 'Â' || c == 'â' || c == 'â' || c == 'Â' || c == 'Â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â' || c == 'â')
+ return LexerTokenType.PL_FRACTION;
+
+ if (c == 'Â')
+ return LexerTokenType.PL_DEGREE;
+
+ if (c == '\'')
+ return LexerTokenType.PL_MINUTE;
+
+ if (c == '"')
+ return LexerTokenType.PL_SECOND;
+
+ if (c.isalpha ())
+ return LexerTokenType.PL_LETTER;
+
+ if (c == 'â')
+ return LexerTokenType.AND;
+
+ if (c == 'â')
+ return LexerTokenType.OR;
+
+ if (c == 'â' || c == 'â')
+ return LexerTokenType.XOR;
+
+ if (c == 'Â' || c == '~')
+ return LexerTokenType.NOT;
+
+ if (c == '+')
+ return LexerTokenType.ADD;
+
+ if (c == '-' || c == 'â' || c == 'â')
+ return LexerTokenType.SUBTRACT;
+
+ if (c == '*' || c == 'Ã')
+ return LexerTokenType.MULTIPLY;
+
+ if (c == '/' || c == 'â' || c == 'Ã')
+ return LexerTokenType.DIVIDE;
+
+ if (c == 'â')
+ return LexerTokenType.L_FLOOR;
+
+ if (c == 'â')
+ return LexerTokenType.R_FLOOR;
+
+ if (c == 'â')
+ return LexerTokenType.L_CEILING;
+
+ if (c == 'â')
+ return LexerTokenType.R_CEILING;
+
+ if (c == 'â')
+ return LexerTokenType.ROOT;
+
+ if (c == 'â')
+ return LexerTokenType.ROOT_3;
+
+ if (c == 'â')
+ return LexerTokenType.ROOT_4;
+
+ if (c == '=')
+ return LexerTokenType.ASSIGN;
+
+ if (c == '(')
+ return LexerTokenType.L_R_BRACKET;
+
+ if (c == ')')
+ return LexerTokenType.R_R_BRACKET;
+
+ if (c == '[')
+ return LexerTokenType.L_S_BRACKET;
+
+ if (c == ']')
+ return LexerTokenType.R_S_BRACKET;
+
+ if (c == '{')
+ return LexerTokenType.L_C_BRACKET;
+
+ if (c == '}')
+ return LexerTokenType.R_C_BRACKET;
+
+ if (c == '|')
+ return LexerTokenType.ABS;
+
+ if (c == '^')
+ return LexerTokenType.POWER;
+
+ if (c == '!')
+ return LexerTokenType.FACTORIAL;
+
+ if (c == '%')
+ return LexerTokenType.PERCENTAGE;
+
+ if (c == ' ' || c == '\r' || c == '\t' || c == '\n')
+ return LexerTokenType.PL_SKIP;
+
+ return LexerTokenType.UNKNOWN;
+ }
+}
+
+/* Structure to hold single token. */
+public class LexerToken
+{
+ public string text; /* Copy of token string. */
+ public uint start_index; /* Start index in original stream. */
+ public uint end_index; /* End index in original stream. */
+ public LexerTokenType type; /* Type of token. */
+}
+
+/* Structure to hold lexer state and all the tokens. */
+public class Lexer
+{
+ private Parser parser; /* Pointer to the parser parser. */
+ private PreLexer prelexer; /* Pre-lexer Pre-lexer is part of lexer. */
+ public List<LexerToken> tokens; /* Pointer to the dynamic array of LexerTokens. */
+ private uint next_token; /* Index of next, to be sent, token. */
+ private int number_base;
+
+ public Lexer (string input, Parser parser, int number_base = 10)
+ {
+ prelexer = new PreLexer (input);
+ tokens = new List<LexerToken> ();
+ next_token = 0;
+ this.parser = parser;
+ this.number_base = number_base;
+ }
+
+ public void scan ()
+ {
+ while (true)
+ {
+ var token = insert_next_token ();
+ tokens.append (token);
+ if (token.type == LexerTokenType.PL_EOS)
+ break;
+ }
+ }
+
+ /* Get next token interface. Will be called by parser to get pointer to next token in token stream. */
+ public LexerToken get_next_token ()
+ {
+ var token = tokens.nth_data (next_token);
+ next_token++;
+ if (next_token >= tokens.length ())
+ next_token = tokens.length ();
+
+ return token;
+ }
+
+ /* Roll back one lexer token. */
+ public void roll_back ()
+ {
+ if (next_token > 0)
+ next_token--;
+ }
+
+ private bool check_if_function ()
+ {
+ var name = prelexer.get_marked_substring ();
+
+ if (parser.function_is_defined (name))
+ return true;
+ else
+ return false;
+ }
+
+ private bool check_if_number ()
+ {
+ int count = 0;
+ var text = prelexer.get_marked_substring ();
+
+ var tmp = mp_set_from_string (text, number_base);
+ if (tmp != null)
+ return true;
+ else
+ {
+ /* Try to rollback several characters to see, if that yields any number. */
+ while (text != "")
+ {
+ tmp = mp_set_from_string (text, number_base);
+ if (tmp != null)
+ return true;
+ count++;
+ prelexer.roll_back ();
+ text = prelexer.get_marked_substring ();
+ }
+
+ /* Undo all rollbacks. */
+ while (count-- > 0)
+ prelexer.get_next_token ();
+
+ return false;
+ }
+ }
+
+ /* Insert generated token to the lexer */
+ private LexerToken insert_token (LexerTokenType type)
+ {
+ var token = new LexerToken ();
+ token.text = prelexer.get_marked_substring ();
+ token.start_index = prelexer.mark_index;
+ token.end_index = prelexer.index;
+ token.type = type;
+
+ return token;
+ }
+
+ /* Generates next token from pre-lexer stream and call insert_token () to insert it at the end. */
+ private LexerToken insert_next_token ()
+ {
+ /* Mark start of next token */
+ prelexer.set_marker ();
+
+ /* Ignore whitespace */
+ var type = prelexer.get_next_token ();
+ while (type == LexerTokenType.PL_SKIP)
+ {
+ prelexer.set_marker ();
+ type = prelexer.get_next_token ();
+ }
+
+ if (type == LexerTokenType.AND || type == LexerTokenType.OR || type == LexerTokenType.XOR || type == LexerTokenType.NOT || type == LexerTokenType.ADD || type == LexerTokenType.SUBTRACT || type == LexerTokenType.MULTIPLY || type == LexerTokenType.DIVIDE || type == LexerTokenType.L_FLOOR || type == LexerTokenType.R_FLOOR || type == LexerTokenType.L_CEILING || type == LexerTokenType.R_CEILING || type == LexerTokenType.ROOT || type == LexerTokenType.ROOT_3 || type == LexerTokenType.ROOT_4 || type == LexerTokenType.ASSIGN || type == LexerTokenType.L_R_BRACKET || type == LexerTokenType.R_R_BRACKET || type == LexerTokenType.L_S_BRACKET || type == LexerTokenType.R_S_BRACKET || type == LexerTokenType.L_C_BRACKET || type == LexerTokenType.R_C_BRACKET || type == LexerTokenType.ABS || type == LexerTokenType.POWER || type == LexerTokenType.FACTORIAL || type == LexerTokenType.PERCENTAGE)
+ return insert_token (type);
+
+ /* [LexerTokenType.PL_SUPER_MINUS][LexerTokenType.PL_SUPER_DIGIT]+ */
+ if (type == LexerTokenType.PL_SUPER_MINUS)
+ {
+ if ((type = prelexer.get_next_token ()) != LexerTokenType.PL_SUPER_DIGIT)
+ {
+ /* ERROR: expected LexerTokenType.PL_SUP_DIGIT */
+ parser.set_error (ErrorCode.MP, prelexer.get_marked_substring ());
+ return insert_token (LexerTokenType.UNKNOWN);
+ }
+
+ /* Get all LexerTokenType.PL_SUPER_DIGITs. */
+ while (prelexer.get_next_token () == LexerTokenType.PL_SUPER_DIGIT);
+ prelexer.roll_back ();
+
+ return insert_token (LexerTokenType.NSUP_NUMBER);
+ }
+
+ /* [LexerTokenType.PL_SUPER_DIGIT]+ */
+ if (type == LexerTokenType.PL_SUPER_DIGIT)
+ {
+ while (prelexer.get_next_token () == LexerTokenType.PL_SUPER_DIGIT);
+ prelexer.roll_back ();
+
+ return insert_token (LexerTokenType.SUP_NUMBER);
+ }
+
+ /* [LexerTokenType.PL_SUB_DIGIT]+ */
+ if (type == LexerTokenType.PL_SUB_DIGIT)
+ {
+ while (prelexer.get_next_token () == LexerTokenType.PL_SUB_DIGIT);
+ prelexer.roll_back ();
+
+ return insert_token (LexerTokenType.SUB_NUMBER);
+ }
+
+ /* [LexerTokenType.PL_FRACTION] */
+ if (type == LexerTokenType.PL_FRACTION)
+ return insert_token (LexerTokenType.NUMBER);
+
+ if (type == LexerTokenType.PL_DIGIT)
+ return insert_digit ();
+
+ if (type == LexerTokenType.PL_DECIMAL)
+ return insert_decimal ();
+
+ if (type == LexerTokenType.PL_HEX)
+ return insert_hex ();
+
+ if (type == LexerTokenType.PL_LETTER)
+ return insert_letter ();
+
+ if (type == LexerTokenType.PL_EOS)
+ return insert_token (LexerTokenType.PL_EOS);
+
+ /* ERROR: Unexpected token */
+ parser.set_error (ErrorCode.INVALID, prelexer.get_marked_substring ());
+
+ return insert_token (LexerTokenType.UNKNOWN);
+ }
+
+ private LexerToken insert_digit ()
+ {
+ var type = prelexer.get_next_token ();
+ while (type == LexerTokenType.PL_DIGIT)
+ type = prelexer.get_next_token ();
+
+ if (type == LexerTokenType.PL_FRACTION)
+ return insert_token (LexerTokenType.NUMBER);
+ else if (type == LexerTokenType.PL_SUB_DIGIT)
+ {
+ while (prelexer.get_next_token () == LexerTokenType.PL_SUB_DIGIT);
+ prelexer.roll_back ();
+ return insert_token (LexerTokenType.NUMBER);
+ }
+ else if (type == LexerTokenType.PL_DEGREE)
+ {
+ type = prelexer.get_next_token ();
+ if (type == LexerTokenType.PL_DIGIT)
+ {
+ while ((type = prelexer.get_next_token ()) == LexerTokenType.PL_DIGIT);
+ if (type == LexerTokenType.PL_DECIMAL)
+ return insert_angle_num_dm ();
+
+ else if (type == LexerTokenType.PL_MINUTE)
+ {
+ type = prelexer.get_next_token ();
+ if (type == LexerTokenType.PL_DIGIT)
+ {
+ while ((type = prelexer.get_next_token ()) == LexerTokenType.PL_DIGIT);
+ if (type == LexerTokenType.PL_DECIMAL)
+ return insert_angle_num_dms ();
+ else if (type == LexerTokenType.PL_SECOND)
+ return insert_token (LexerTokenType.NUMBER);
+ else
+ {
+ /* ERROR: expected LexerTokenType.PL_SECOND */
+ parser.set_error (ErrorCode.MP, prelexer.get_marked_substring ());
+ return insert_token (LexerTokenType.UNKNOWN);
+ }
+ }
+ else if (type == LexerTokenType.PL_DECIMAL)
+ return insert_angle_num_dms ();
+ else
+ {
+ prelexer.roll_back ();
+ return insert_token (LexerTokenType.NUMBER);
+ }
+ }
+ else
+ {
+ /* ERROR: expected LexerTokenType.PL_MINUTE | LexerTokenType.PL_DIGIT */
+ parser.set_error (ErrorCode.MP, prelexer.get_marked_substring ());
+ return insert_token (LexerTokenType.UNKNOWN);
+ }
+ }
+ else if (type == LexerTokenType.PL_DECIMAL)
+ return insert_angle_num_dm ();
+ else
+ return insert_token (LexerTokenType.NUMBER);
+ }
+ else if (type == LexerTokenType.PL_DECIMAL)
+ return insert_decimal ();
+ else if (type == LexerTokenType.PL_HEX)
+ return insert_hex_dec ();
+ else
+ {
+ prelexer.roll_back ();
+ return insert_token (LexerTokenType.NUMBER);
+ }
+ }
+
+ private LexerToken insert_angle_num_dm ()
+ {
+ var type = prelexer.get_next_token ();
+ if (type != LexerTokenType.PL_DIGIT)
+ {
+ /* ERROR: expected LexerTokenType.PL_DIGIT */
+ parser.set_error (ErrorCode.MP, prelexer.get_marked_substring ());
+ return insert_token (LexerTokenType.UNKNOWN);
+ }
+
+ while (type == LexerTokenType.PL_DIGIT);
+ type = prelexer.get_next_token ();
+
+ if (type == LexerTokenType.PL_MINUTE)
+ return insert_token (LexerTokenType.NUMBER);
+ else
+ {
+ /* ERROR: expected LexerTokenType.PL_MINUTE */
+ parser.set_error (ErrorCode.MP, prelexer.get_marked_substring ());
+ return insert_token (LexerTokenType.UNKNOWN);
+ }
+ }
+
+ private LexerToken insert_angle_num_dms ()
+ {
+ var type = prelexer.get_next_token ();
+ if (type != LexerTokenType.PL_DIGIT)
+ {
+ /* ERROR: expected LexerTokenType.PL_DIGIT */
+ parser.set_error (ErrorCode.MP, prelexer.get_marked_substring ());
+ return insert_token (LexerTokenType.UNKNOWN);
+ }
+ while ((type = prelexer.get_next_token ()) == LexerTokenType.PL_DIGIT);
+ if (type == LexerTokenType.PL_SECOND)
+ return insert_token (LexerTokenType.NUMBER);
+ else
+ {
+ /* ERROR: expected LexerTokenType.PL_SECOND */
+ parser.set_error (ErrorCode.MP, prelexer.get_marked_substring ());
+ return insert_token (LexerTokenType.UNKNOWN);
+ }
+ }
+
+ private LexerToken insert_decimal ()
+ {
+ var type = prelexer.get_next_token ();
+ if (type == LexerTokenType.PL_DIGIT)
+ {
+ while ((type = prelexer.get_next_token ()) == LexerTokenType.PL_DIGIT);
+ if (type == LexerTokenType.PL_DEGREE)
+ return insert_token (LexerTokenType.NUMBER);
+ else if (type == LexerTokenType.PL_HEX)
+ return insert_decimal_hex ();
+ else if (type == LexerTokenType.PL_SUB_DIGIT)
+ {
+ while (prelexer.get_next_token () == LexerTokenType.PL_SUB_DIGIT);
+ prelexer.roll_back ();
+ return insert_token (LexerTokenType.NUMBER);
+ }
+ else
+ {
+ prelexer.roll_back ();
+ return insert_token (LexerTokenType.NUMBER);
+ }
+ }
+ else if (type == LexerTokenType.PL_HEX)
+ return insert_decimal_hex ();
+ else
+ {
+ /* ERROR: expected LexerTokenType.PL_DIGIT | LexerTokenType.PL_HEX */
+ parser.set_error (ErrorCode.MP, prelexer.get_marked_substring ());
+ return insert_token (LexerTokenType.UNKNOWN);
+ }
+ }
+
+ private LexerToken insert_hex ()
+ {
+ var type = prelexer.get_next_token ();
+ while (type == LexerTokenType.PL_HEX)
+ type = prelexer.get_next_token ();
+
+ if (type == LexerTokenType.PL_DIGIT)
+ return insert_hex_dec ();
+ else if (type == LexerTokenType.PL_DECIMAL)
+ return insert_decimal_hex ();
+ else if (type == LexerTokenType.PL_SUB_DIGIT)
+ {
+ while (prelexer.get_next_token () == LexerTokenType.PL_SUB_DIGIT);
+ prelexer.roll_back ();
+
+ if (check_if_number ())
+ return insert_token (LexerTokenType.NUMBER);
+ else
+ {
+ if (check_if_function ())
+ return insert_token (LexerTokenType.FUNCTION);
+ else
+ return insert_token (LexerTokenType.VARIABLE);
+ }
+ }
+ else if (type == LexerTokenType.PL_LETTER)
+ return insert_letter ();
+ else
+ {
+ prelexer.roll_back ();
+ if (check_if_number ())
+ return insert_token (LexerTokenType.NUMBER);
+ else
+ {
+ if (check_if_function ())
+ return insert_token (LexerTokenType.FUNCTION);
+ else
+ return insert_token (LexerTokenType.VARIABLE);
+ }
+ }
+ }
+
+ private LexerToken insert_hex_dec ()
+ {
+ var type = prelexer.get_next_token ();
+ while (type == LexerTokenType.PL_DIGIT || type == LexerTokenType.PL_HEX)
+ type = prelexer.get_next_token ();
+
+ if (type == LexerTokenType.PL_DECIMAL)
+ return insert_decimal_hex ();
+ else if (type == LexerTokenType.PL_SUB_DIGIT)
+ {
+ while (prelexer.get_next_token () == LexerTokenType.PL_SUB_DIGIT);
+ prelexer.roll_back ();
+ return insert_token (LexerTokenType.NUMBER);
+ }
+ else
+ {
+ if (check_if_number ())
+ return insert_token (LexerTokenType.NUMBER);
+ /* ERROR: expected LexerTokenType.PL_DECIMAL | LexerTokenType.PL_DIGIT | LexerTokenType.PL_HEX */
+ parser.set_error (ErrorCode.MP, prelexer.get_marked_substring ());
+ return insert_token (LexerTokenType.UNKNOWN);
+ }
+ }
+
+ private LexerToken insert_decimal_hex ()
+ {
+ /* Make up of digits and hexadecimal characters */
+ var type = prelexer.get_next_token ();
+ while (type == LexerTokenType.PL_DIGIT || type == LexerTokenType.PL_HEX)
+ type = prelexer.get_next_token ();
+
+ /* Allow a subdigit suffix */
+ while (type == LexerTokenType.PL_SUB_DIGIT)
+ type = prelexer.get_next_token ();
+
+ prelexer.roll_back ();
+
+ return insert_token (LexerTokenType.NUMBER);
+ }
+
+ private LexerToken insert_letter ()
+ {
+ /* Get string of letters */
+ var type = prelexer.get_next_token ();
+ while (type == LexerTokenType.PL_LETTER || type == LexerTokenType.PL_HEX)
+ type = prelexer.get_next_token ();
+
+ /* Allow a subdigit suffix */
+ while (type == LexerTokenType.PL_SUB_DIGIT)
+ type = prelexer.get_next_token ();
+
+ prelexer.roll_back ();
+
+ var name = prelexer.get_marked_substring ().down ();
+ if (name == "mod")
+ return insert_token (LexerTokenType.MOD);
+ if (name == "and")
+ return insert_token (LexerTokenType.AND);
+ if (name == "or")
+ return insert_token (LexerTokenType.OR);
+ if (name == "xor")
+ return insert_token (LexerTokenType.XOR);
+ if (name == "not")
+ return insert_token (LexerTokenType.NOT);
+ if (name == "in")
+ return insert_token (LexerTokenType.IN);
+ if (check_if_function ())
+ return insert_token (LexerTokenType.FUNCTION);
+ else
+ return insert_token (LexerTokenType.VARIABLE);
+ }
+}
diff --git a/src/equation-parser.vala b/src/equation-parser.vala
new file mode 100644
index 0000000..65ddb56
--- /dev/null
+++ b/src/equation-parser.vala
@@ -0,0 +1,1703 @@
+/*
+ * Copyright (C) 2012 Arth Patel
+ * Copyright (C) 2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+/* Operator Associativity. */
+public enum Associativity
+{
+ LEFT,
+ RIGHT
+}
+
+/* Operator Precedence. */
+private enum Precedence
+{
+ UNKNOWN = 0,
+ ADD_SUBTRACT = 1,
+ MULTIPLY = 2,
+ MOD = 3,
+ DIVIDE = 4,
+ NOT = 5,
+ ROOT = 6,
+ FUNCTION = 7,
+ BOOLEAN = 8,
+ PERCENTAGE = 9,
+ /* UNARY_MINUS and POWER must have same precedence. */
+ UNARY_MINUS = 10,
+ POWER = 10,
+ FACTORIAL = 11,
+ NUMBER_VARIABLE = 12,
+ /* DEPTH should be always at the bottom. It stops node jumping off the current depth level. */
+ DEPTH
+}
+
+/* ParseNode structure for parse tree. */
+public class ParseNode
+{
+ public Parser parser;
+ public ParseNode? parent = null;
+ public ParseNode? left = null;
+ public ParseNode? right = null;
+ public LexerToken token;
+ public uint precedence;
+ public Associativity associativity;
+ public string? value;
+
+ public ParseNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity, string? value = null)
+ {
+ this.parser = parser;
+ this.token = token;
+ this.precedence = precedence;
+ this.associativity = associativity;
+ this.value = value;
+ }
+
+ public virtual Number? solve ()
+ {
+ return null;
+ }
+}
+
+public abstract class RNode : ParseNode
+{
+ public RNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number? solve ()
+ {
+ var r = right.solve ();
+ if (r == null)
+ return null;
+ return solve_r (r);
+ }
+
+ public abstract Number solve_r (Number r);
+}
+
+public abstract class LRNode : ParseNode
+{
+ public LRNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number? solve ()
+ {
+ var l = left.solve ();
+ var r = right.solve ();
+ if (l == null || r == null)
+ return null;
+ return solve_lr (l, r);
+ }
+
+ public abstract Number solve_lr (Number left, Number r);
+}
+
+public class ConstantNode : ParseNode
+{
+ public ConstantNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number? solve ()
+ {
+ return mp_set_from_string (token.text, parser.number_base);
+ }
+}
+
+public class AssignNode : RNode
+{
+ public AssignNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_r (Number r)
+ {
+ parser.set_variable (left.token.text, r);
+ return r;
+ }
+}
+
+public class NameNode : ParseNode
+{
+ public NameNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity, string? text = null)
+ {
+ base (parser, token, precedence, associativity, text);
+ }
+}
+
+public class VariableNode : ParseNode
+{
+ public VariableNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number? solve ()
+ {
+ /* If defined, then get the variable */
+ var ans = parser.get_variable (token.text);
+ if (ans != null)
+ return ans;
+
+ /* If has more than one character then assume a multiplication of variables */
+ // FIXME: Do this in the lexer
+ var value = new Number.integer (1);
+ var index = 0;
+ unichar c;
+ while (token.text.get_next_char (ref index, out c))
+ {
+ var t = parser.get_variable (c.to_string ());
+ if (t == null)
+ {
+ parser.set_error (ErrorCode.UNKNOWN_VARIABLE, token.text);
+ return null;
+ }
+ value = value.multiply (t);
+ }
+
+ return value;
+ }
+}
+
+public class VariableWithPowerNode : ParseNode
+{
+ public VariableWithPowerNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity, string text)
+ {
+ base (parser, token, precedence, associativity, text);
+ }
+
+ public override Number? solve ()
+ {
+ var pow = super_atoi (value);
+
+ value = null;
+
+ /* If defined, then get the variable */
+ var ans = parser.get_variable (token.text);
+ if (ans != null)
+ return ans.xpowy_integer (pow);
+
+ /* If has more than one character then assume a multiplication of variables */
+ // FIXME: Do in lexer
+ var value = new Number.integer (1);
+ var index = 0;
+ unichar c;
+ while (token.text.get_next_char (ref index, out c))
+ {
+ var t = parser.get_variable (c.to_string ());
+ if (t == null)
+ {
+ parser.set_error (ErrorCode.UNKNOWN_VARIABLE, token.text);
+ return null;
+ }
+
+ /* If last term do power */
+ var i = index;
+ unichar next;
+ if (!token.text.get_next_char (ref i, out next))
+ t = t.xpowy_integer (pow);
+ value = value.multiply (t);
+ }
+
+ return value;
+ }
+}
+
+public class FunctionNode : RNode
+{
+ public FunctionNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_r (Number r)
+ {
+ var ans = parser.get_function (token.text, r);
+ if (ans == null)
+ parser.set_error (ErrorCode.UNKNOWN_FUNCTION, token.text);
+
+ return ans;
+ }
+}
+
+public class FunctionWithPowerNode : ParseNode
+{
+ public FunctionWithPowerNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity, string text)
+ {
+ base (parser, token, precedence, associativity, text);
+ }
+
+ public override Number? solve ()
+ {
+ var val = right.solve ();
+ if (val == null)
+ {
+ value = null;
+ return null;
+ }
+ var tmp = parser.get_function (token.text, val);
+ if (tmp == null)
+ {
+ value = null;
+ parser.set_error (ErrorCode.UNKNOWN_FUNCTION, token.text);
+ return null;
+ }
+
+ var pow = super_atoi (value);
+ value = null;
+
+ return tmp.xpowy_integer (pow);
+ }
+}
+
+public class FunctionWithNegativePowerNode : ParseNode
+{
+ public FunctionWithNegativePowerNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity, string text)
+ {
+ base (parser, token, precedence, associativity, text);
+ }
+
+ public override Number? solve ()
+ {
+ var val = right.solve ();
+ if (val == null)
+ {
+ value = null;
+ return null;
+ }
+ var inv_name = token.text + "âÂ";
+ var tmp = parser.get_function (inv_name, val);
+ if (tmp == null)
+ {
+ value = null;
+ parser.set_error (ErrorCode.UNKNOWN_FUNCTION, token.text);
+ return null;
+ }
+
+ var pow = super_atoi (value);
+ value = null;
+
+ return tmp.xpowy_integer (-pow);
+ }
+}
+
+public class UnaryMinusNode : RNode
+{
+ public UnaryMinusNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_r (Number r)
+ {
+ return r.invert_sign ();
+ }
+}
+
+public class AbsoluteValueNode : RNode
+{
+ public AbsoluteValueNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_r (Number r)
+ {
+ return r.abs ();
+ }
+}
+
+public class FloorNode : RNode
+{
+ public FloorNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_r (Number r)
+ {
+ return r.floor ();
+ }
+}
+
+public class CeilingNode : RNode
+{
+ public CeilingNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_r (Number r)
+ {
+ return r.ceiling ();
+ }
+}
+
+public class FractionalComponentNode : RNode
+{
+ public FractionalComponentNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_r (Number r)
+ {
+ return r.fractional_part ();
+ }
+}
+
+public class RoundNode : RNode
+{
+ public RoundNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_r (Number r)
+ {
+ return r.round ();
+ }
+}
+
+public class PercentNode : RNode
+{
+ public PercentNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_r (Number r)
+ {
+ return r.divide_integer (100);
+ }
+}
+
+public class FactorialNode : RNode
+{
+ public FactorialNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_r (Number r)
+ {
+ return r.factorial ();
+ }
+}
+
+public class AddNode : LRNode
+{
+ public bool do_percentage = false;
+
+ public AddNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_lr (Number l, Number r)
+ {
+ if (do_percentage)
+ {
+ var per = r.add (new Number.integer (100));
+ per = per.divide_integer (100);
+ return l.multiply (per);
+ }
+ else
+ return l.add (r);
+ }
+}
+
+public class SubtractNode : LRNode
+{
+ public bool do_percentage = false;
+
+ public SubtractNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_lr (Number l, Number r)
+ {
+ if (do_percentage)
+ {
+ var per = r.add (new Number.integer (-100));
+ per = per.divide_integer (-100);
+ return l.multiply (per);
+ }
+ else
+ return l.subtract (r);
+ }
+}
+
+public class MultiplyNode : LRNode
+{
+ public MultiplyNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_lr (Number l, Number r)
+ {
+ return l.multiply (r);
+ }
+}
+
+public class DivideNode : LRNode
+{
+ public DivideNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_lr (Number l, Number r)
+ {
+ return l.divide (r);
+ }
+}
+
+public class ModulusDivideNode : LRNode
+{
+ public ModulusDivideNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_lr (Number l, Number r)
+ {
+ return l.modulus_divide (r);
+ }
+}
+
+public class RootNode : RNode
+{
+ private int n;
+
+ public RootNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity, int n)
+ {
+ base (parser, token, precedence, associativity);
+ this.n = n;
+ }
+
+ public override Number solve_r (Number r)
+ {
+ return r.root (n);
+ }
+}
+
+public class XPowYNode : LRNode
+{
+ public XPowYNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_lr (Number l, Number r)
+ {
+ return l.xpowy (r);
+ }
+}
+
+public class XPowYIntegerNode : ParseNode
+{
+ public XPowYIntegerNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number? solve ()
+ {
+ var val = left.solve ();
+ var pow = super_atoi (right.token.text);
+ if (val == null)
+ return null;
+
+ return val.xpowy_integer (pow);
+ }
+}
+
+public class NotNode : RNode
+{
+ public NotNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_r (Number r)
+ {
+ if (!mp_is_overflow (r, parser.wordlen))
+ {
+ parser.set_error (ErrorCode.OVERFLOW, null);
+ return new Number.integer (0);
+ }
+
+ return r.not (parser.wordlen);
+ }
+}
+
+public class AndNode : LRNode
+{
+ public AndNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_lr (Number l, Number r)
+ {
+ return l.and (r);
+ }
+}
+
+public class OrNode : LRNode
+{
+ public OrNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_lr (Number l, Number r)
+ {
+ return l.or (r);
+ }
+}
+
+public class XorNode : LRNode
+{
+ public XorNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_lr (Number l, Number r)
+ {
+ return l.xor (r);
+ }
+}
+
+public class ConvertNode : LRNode
+{
+ public ConvertNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number solve_lr (Number l, Number r)
+ {
+ string from;
+ if (left.value != null)
+ {
+ from = left.value;
+ left.value = null;
+ }
+ else
+ from = left.token.text;
+
+ string to;
+ if (right.value != null)
+ {
+ to = right.value;
+ right.value = null;
+ }
+ else
+ to = right.token.text;
+
+ var tmp = new Number.integer (1);
+
+ var ans = parser.convert (tmp, from, to);
+ if (ans == null)
+ parser.set_error (ErrorCode.UNKNOWN_CONVERSION, null);
+
+ return ans;
+ }
+}
+
+public class ConvertNumberNode : ParseNode
+{
+ public ConvertNumberNode (Parser parser, LexerToken? token, uint precedence, Associativity associativity)
+ {
+ base (parser, token, precedence, associativity);
+ }
+
+ public override Number? solve ()
+ {
+ string from;
+ if (left.value != null)
+ {
+ from = left.value;
+ left.value = null;
+ }
+ else
+ from = left.token.text;
+
+ string to;
+ if (right.value != null)
+ {
+ to = right.value;
+ right.value = null;
+ }
+ else
+ to = right.token.text;
+
+ var tmp = mp_set_from_string (left.left.token.text, parser.number_base);
+ if (tmp == null)
+ return null;
+
+ var ans = parser.convert (tmp, from, to);
+ if (ans == null)
+ parser.set_error (ErrorCode.UNKNOWN_CONVERSION, null);
+
+ return ans;
+ }
+}
+
+public class Parser
+{
+ private ParseNode root;
+ private ParseNode right_most;
+ private Lexer lexer;
+ public int number_base;
+ public int wordlen;
+ private uint depth_level;
+ private ErrorCode error;
+ private string error_token;
+
+ public Parser (string input, int number_base, int wordlen)
+ {
+ lexer = new Lexer (input, this, number_base);
+ root = null;
+ depth_level = 0;
+ right_most = null;
+ this.number_base = number_base;
+ this.wordlen = wordlen;
+ error = ErrorCode.NONE;
+ error_token = null;
+ }
+
+ public void set_error (ErrorCode errorno, string? token)
+ {
+ error = errorno;
+ error_token = token;
+ }
+
+ public virtual bool variable_is_defined (string name)
+ {
+ return false;
+ }
+
+ public virtual Number? get_variable (string name)
+ {
+ return null;
+ }
+
+ public virtual void set_variable (string name, Number x)
+ {
+ }
+
+ public virtual bool function_is_defined (string name)
+ {
+ return false;
+ }
+
+ public virtual Number? get_function (string name, Number x)
+ {
+ return null;
+ }
+
+ public virtual Number? convert (Number x, string x_units, string z_units)
+ {
+ return null;
+ }
+
+ /* Start parsing input string. And call evaluate on success. */
+ public Number? parse (out ErrorCode error_code, out string? error_token)
+ {
+ /* Scan string and split into tokens */
+ lexer.scan ();
+
+ /* Parse tokens */
+ var ret = statement ();
+
+ var token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.ASSIGN)
+ {
+ token = lexer.get_next_token ();
+ if (token.type != LexerTokenType.PL_EOS)
+ {
+ /* Full string is not parsed. */
+ if (error == 0)
+ set_error (ErrorCode.INVALID, token.text);
+
+ error_code = error;
+ error_token = this.error_token;
+ return null;
+ }
+ }
+ if (token.type != LexerTokenType.PL_EOS)
+ {
+ /* Full string is not parsed. */
+ if (error == 0)
+ set_error (ErrorCode.INVALID, token.text);
+
+ error_code = error;
+ error_token = this.error_token;
+ return null;
+ }
+
+ /* Input can't be parsed with grammar. */
+ if (!ret)
+ {
+ error_code = error;
+ error_token = this.error_token;
+ return null;
+ }
+ var ans = root.solve ();
+ if (ans == null)
+ {
+ error_code = ErrorCode.INVALID;
+ error_token = null;
+ return null;
+ }
+
+ error_code = ErrorCode.NONE;
+ error_token = null;
+ return ans;
+ }
+
+ /* Converts LexerTokenType to Precedence value. */
+ private Precedence get_precedence (LexerTokenType type)
+ {
+ /* WARNING: This function doesn't work for Unary Plus and Unary Minus. Use their precedence directly while inserting them in tree. */
+ if (type == LexerTokenType.ADD || type == LexerTokenType.SUBTRACT)
+ return Precedence.ADD_SUBTRACT;
+ if (type == LexerTokenType.MULTIPLY)
+ return Precedence.MULTIPLY;
+ if (type == LexerTokenType.MOD)
+ return Precedence.MOD;
+ if (type == LexerTokenType.DIVIDE)
+ return Precedence.DIVIDE;
+ if (type == LexerTokenType.NOT)
+ return Precedence.NOT;
+ if (type == LexerTokenType.ROOT || type == LexerTokenType.ROOT_3 || type == LexerTokenType.ROOT_4)
+ return Precedence.ROOT;
+ if (type == LexerTokenType.FUNCTION)
+ return Precedence.FUNCTION;
+ if (type == LexerTokenType.AND || type == LexerTokenType.OR || type == LexerTokenType.XOR)
+ return Precedence.BOOLEAN;
+ if (type == LexerTokenType.PERCENTAGE)
+ return Precedence.PERCENTAGE;
+ if (type == LexerTokenType.POWER)
+ return Precedence.POWER;
+ if (type == LexerTokenType.FACTORIAL)
+ return Precedence.FACTORIAL;
+ if (type == LexerTokenType.NUMBER || type == LexerTokenType.VARIABLE)
+ return Precedence.NUMBER_VARIABLE;
+ return Precedence.UNKNOWN;
+ }
+
+ /* Return associativity of specific token type from precedence. */
+ private Associativity get_associativity_p (Precedence type)
+ {
+ if (type == Precedence.BOOLEAN || type == Precedence.DIVIDE || type == Precedence.MOD || type == Precedence.MULTIPLY || type == Precedence.ADD_SUBTRACT)
+ return Associativity.LEFT;
+ if (type == Precedence.POWER)
+ return Associativity.RIGHT;
+ /* For all remaining / non-associative operators, return Left Associativity. */
+ return Associativity.LEFT;
+ }
+
+ /* Return associativity of specific token by converting it to precedence first. */
+ private Associativity get_associativity (LexerToken token)
+ {
+ return get_associativity_p (get_precedence (token.type));
+ }
+
+ /* Generate precedence for a node from precedence value. Includes depth_level. */
+ private uint make_precedence_p (Precedence p)
+ {
+ return p + (depth_level * Precedence.DEPTH);
+ }
+
+ /* Generate precedence for a node from lexer token type. Includes depth_level. */
+ private uint make_precedence_t (LexerTokenType type)
+ {
+ return get_precedence (type) + (depth_level * Precedence.DEPTH);
+ }
+
+ /* Compares two nodes to decide, which will be parent and which will be child. */
+ private bool cmp_nodes (ParseNode? left, ParseNode? right)
+ {
+ /* Return values:
+ * true = right goes up (near root) in parse tree.
+ * false = left goes up (near root) in parse tree.
+ */
+ if (left == null)
+ return false;
+ if (left.precedence > right.precedence)
+ return true;
+ else if (left.precedence < right.precedence)
+ return false;
+ else
+ return right.associativity != Associativity.RIGHT;
+ }
+
+ /* Unified interface (unary and binary nodes) to insert node into parse tree. */
+ private void insert_into_tree_all (ParseNode node, bool unary_function)
+ {
+ if (root == null)
+ {
+ root = node;
+ right_most = root;
+ return;
+ }
+ ParseNode tmp = right_most;
+ while (cmp_nodes (tmp, node))
+ tmp = tmp.parent;
+
+ if (unary_function)
+ {
+ /* If tmp is null, that means, we have to insert new node at root. */
+ if (tmp == null)
+ {
+ node.right = root;
+ node.right.parent = node;
+
+ root = node;
+ }
+ else
+ {
+ node.right = tmp.right;
+ if (node.right != null)
+ node.right.parent = node;
+
+ tmp.right = node;
+ if (tmp.right != null)
+ tmp.right.parent = tmp;
+
+ }
+ right_most = node;
+ while (right_most.right != null)
+ right_most = right_most.right;
+ }
+ else
+ {
+ /* If tmp is null, that means, we have to insert new node at root. */
+ if (tmp == null)
+ {
+ node.left = root;
+ node.left.parent = node;
+
+ root = node;
+ }
+ else
+ {
+ node.left = tmp.right;
+ if (node.left != null)
+ node.left.parent = node;
+
+ tmp.right = node;
+ if (tmp.right != null)
+ tmp.right.parent = tmp;
+
+ }
+ right_most = node;
+ }
+ }
+
+ /* Insert binary node into the parse tree. */
+ private void insert_into_tree (ParseNode node)
+ {
+ insert_into_tree_all (node, false);
+ }
+
+ /* Insert unary node into the parse tree. */
+ private void insert_into_tree_unary (ParseNode node)
+ {
+ insert_into_tree_all (node, true);
+ }
+
+ /* Recursive call to free every node of parse-tree. */
+ private void destroy_all_nodes (ParseNode node)
+ {
+ if (node == null)
+ return;
+
+ destroy_all_nodes (node.left);
+ destroy_all_nodes (node.right);
+ /* Don't call free for tokens, as they are allocated and freed in lexer. */
+ /* WARNING: If node.value is freed elsewhere, please assign it null before calling destroy_all_nodes (). */
+ }
+
+ /* LL (*) parser. Lookahead count depends on tokens. Handle with care. :P */
+
+ /* Check if string "name" is a valid variable for given Parser. It is the same code, used to get the value of variable in parserfunc.c. */
+ private bool check_variable (string name)
+ {
+ /* If defined, then get the variable */
+ if (variable_is_defined (name))
+ return true;
+
+ /* If has more than one character then assume a multiplication of variables */
+ var index = 0;
+ unichar c;
+ while (name.get_next_char (ref index, out c))
+ {
+ if (!variable_is_defined (c.to_string ()))
+ return false;
+ }
+
+ return true;
+ }
+
+ private bool statement ()
+ {
+ var token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.VARIABLE)
+ {
+ var token_old = token;
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.ASSIGN)
+ {
+ insert_into_tree (new NameNode (this, token_old, make_precedence_p (Precedence.NUMBER_VARIABLE), get_associativity (token_old)));
+ insert_into_tree (new AssignNode (this, token, 0, get_associativity (token)));
+
+ if (!expression ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.IN)
+ {
+ lexer.roll_back ();
+ lexer.roll_back ();
+
+ if (!unit ())
+ return false;
+ lexer.get_next_token ();
+
+ insert_into_tree (new ConvertNode (this, token, 0, get_associativity (token)));
+
+ if (!unit ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.SUP_NUMBER)
+ {
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.IN)
+ {
+ lexer.roll_back ();
+ lexer.roll_back ();
+ lexer.roll_back ();
+ if (!unit ())
+ return false;
+ lexer.get_next_token ();
+
+ insert_into_tree (new ConvertNode (this, token, 0, get_associativity (token)));
+
+ if (!unit ())
+ return false;
+
+ return true;
+ }
+ else
+ {
+ lexer.roll_back ();
+ lexer.roll_back ();
+ lexer.roll_back ();
+
+ if (!expression ())
+ return false;
+
+ return true;
+ }
+ }
+ else
+ {
+ lexer.roll_back ();
+ lexer.roll_back ();
+
+ if (!expression ())
+ return false;
+
+ return true;
+ }
+ }
+ else if (token.type == LexerTokenType.NUMBER)
+ {
+ var token_old = token;
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.VARIABLE)
+ {
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.IN)
+ {
+ lexer.roll_back ();
+ lexer.roll_back ();
+
+ insert_into_tree (new ConstantNode (this, token_old, make_precedence_t (token_old.type), get_associativity (token)));
+
+ if (!unit ())
+ return false;
+
+ token = lexer.get_next_token ();
+ insert_into_tree (new ConvertNumberNode (this, token, 0, get_associativity (token)));
+
+ if (!unit ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.SUP_NUMBER)
+ {
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.IN)
+ {
+ lexer.roll_back ();
+ lexer.roll_back ();
+ lexer.roll_back ();
+
+ insert_into_tree (new ConstantNode (this, token_old, make_precedence_t (token_old.type), get_associativity (token)));
+
+ if (!unit ())
+ return false;
+ token = lexer.get_next_token ();
+
+ insert_into_tree (new ConvertNumberNode (this, token, 0, get_associativity (token)));
+
+ if (!unit ())
+ return false;
+ return true;
+ }
+ else
+ {
+ lexer.roll_back ();
+ lexer.roll_back ();
+ lexer.roll_back ();
+ lexer.roll_back ();
+ if (!expression ())
+ return false;
+ return true;
+ }
+ }
+ else
+ {
+ lexer.roll_back ();
+ lexer.roll_back ();
+ lexer.roll_back ();
+ if (!expression ())
+ return false;
+ return true;
+ }
+ }
+ else
+ {
+ lexer.roll_back ();
+ lexer.roll_back ();
+ if (!expression ())
+ return false;
+ return true;
+ }
+ }
+ else
+ {
+ lexer.roll_back ();
+ if (!expression ())
+ return false;
+ return true;
+ }
+ }
+
+ private bool unit ()
+ {
+ var token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.VARIABLE)
+ {
+ var token_old = token;
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.SUP_NUMBER)
+ {
+ insert_into_tree (new NameNode (this, token_old, make_precedence_t (token_old.type), get_associativity (token_old), token_old.text + token.text));
+ return true;
+ }
+ else
+ {
+ lexer.roll_back ();
+ insert_into_tree (new NameNode (this, token_old, make_precedence_t (token_old.type), get_associativity (token_old)));
+ return true;
+ }
+ }
+ else
+ {
+ lexer.roll_back ();
+ return false;
+ }
+ }
+
+ private bool expression ()
+ {
+ if (!expression_1 ())
+ return false;
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+
+ private bool expression_1 ()
+ {
+ var token = lexer.get_next_token ();
+
+ if (token.type == LexerTokenType.PL_EOS || token.type == LexerTokenType.ASSIGN)
+ {
+ lexer.roll_back ();
+ return false;
+ }
+
+ if (token.type == LexerTokenType.L_R_BRACKET)
+ {
+ depth_level++;
+
+ if (!expression ())
+ return false;
+
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.R_R_BRACKET)
+ {
+ depth_level--;
+ return true;
+ }
+ //Expected ")" here...
+ else
+ return false;
+ }
+ else if (token.type == LexerTokenType.L_S_BRACKET)
+ {
+ depth_level++;
+
+ /* Give round, preference of Precedence.UNKNOWN aka 0, to keep it on the top of expression. */
+
+ insert_into_tree_unary (new RoundNode (this, token, make_precedence_p (Precedence.UNKNOWN), get_associativity (token)));
+
+ if (!expression ())
+ return false;
+
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.R_S_BRACKET)
+ {
+ depth_level--;
+ return true;
+ }
+ else
+ //Expected "]" here...
+ return false;
+ }
+ else if (token.type == LexerTokenType.L_C_BRACKET)
+ {
+ depth_level++;
+
+ /* Give fraction, preference of Precedence.UNKNOWN aka 0, to keep it on the top of expression. */
+
+ insert_into_tree_unary (new FractionalComponentNode (this, token, make_precedence_p (Precedence.UNKNOWN), get_associativity (token)));
+
+ if (!expression ())
+ return false;
+
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.R_C_BRACKET)
+ {
+ depth_level--;
+ return true;
+ }
+ //Expected "}" here...
+ else
+ return false;
+ }
+ else if (token.type == LexerTokenType.ABS)
+ {
+ depth_level++;
+
+ /* Give abs, preference of Precedence.UNKNOWN aka 0, to keep it on the top of expression. */
+
+ insert_into_tree_unary (new AbsoluteValueNode (this, token, make_precedence_p (Precedence.UNKNOWN), get_associativity (token)));
+
+ if (!expression ())
+ return false;
+
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.ABS)
+ {
+ depth_level--;
+ return true;
+ }
+ //Expected "|" here...
+ else
+ return false;
+ }
+ else if (token.type == LexerTokenType.NOT)
+ {
+ insert_into_tree_unary (new NotNode (this, token, make_precedence_p (Precedence.NOT), get_associativity (token)));
+
+ if (!expression ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.NUMBER)
+ {
+ insert_into_tree (new ConstantNode (this, token, make_precedence_t (token.type), get_associativity (token)));
+
+ token = lexer.get_next_token ();
+ lexer.roll_back ();
+
+ if (token.type == LexerTokenType.FUNCTION || token.type == LexerTokenType.VARIABLE || token.type == LexerTokenType.SUB_NUMBER || token.type == LexerTokenType.ROOT || token.type == LexerTokenType.ROOT_3 || token.type == LexerTokenType.ROOT_4)
+ {
+ insert_into_tree (new MultiplyNode (this, null, make_precedence_p (Precedence.MULTIPLY), get_associativity_p (Precedence.MULTIPLY)));
+
+ if (!variable ())
+ return false;
+ else
+ return true;
+ }
+ else
+ return true;
+ }
+ else if (token.type == LexerTokenType.L_FLOOR)
+ {
+ depth_level++;
+ /* Give floor, preference of Precedence.UNKNOWN aka 0, to keep it on the top of expression. */
+
+ insert_into_tree_unary (new FloorNode (this, null, make_precedence_p (Precedence.UNKNOWN), get_associativity_p (Precedence.UNKNOWN)));
+
+ if (!expression ())
+ return false;
+
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.R_FLOOR)
+ {
+ depth_level--;
+ return true;
+ }
+ //Expected â here...
+ else
+ return false;
+ }
+ else if (token.type == LexerTokenType.L_CEILING)
+ {
+ depth_level++;
+ /* Give ceiling, preference of Precedence.UNKNOWN aka 0, to keep it on the top of expression. */
+
+ insert_into_tree_unary (new CeilingNode (this, null, make_precedence_p (Precedence.UNKNOWN), get_associativity_p (Precedence.UNKNOWN)));
+
+ if (!expression ())
+ return false;
+
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.R_CEILING)
+ {
+ depth_level--;
+ return true;
+ }
+ //Expected â here...
+ else
+ return false;
+ }
+ else if (token.type == LexerTokenType.SUBTRACT)
+ {
+ insert_into_tree_unary (new UnaryMinusNode (this, token, make_precedence_p (Precedence.UNARY_MINUS), get_associativity_p (Precedence.UNARY_MINUS)));
+
+ if (!expression_1 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.ADD)
+ {
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.NUMBER)
+ {
+ /* Ignore ADD. It is not required. */
+ insert_into_tree (new ConstantNode (this, token, make_precedence_t (token.type), get_associativity (token)));
+ return true;
+ }
+ else
+ return false;
+ }
+ else
+ {
+ lexer.roll_back ();
+ if (!variable ())
+ return false;
+ else
+ return true;
+ }
+ }
+
+ private bool expression_2 ()
+ {
+ var token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.L_R_BRACKET)
+ {
+ insert_into_tree (new MultiplyNode (this, null, make_precedence_p (Precedence.MULTIPLY), get_associativity_p (Precedence.MULTIPLY)));
+
+ depth_level++;
+ if (!expression ())
+ return false;
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.R_R_BRACKET)
+ {
+ depth_level--;
+
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else
+ return false;
+ }
+ else if (token.type == LexerTokenType.POWER)
+ {
+ insert_into_tree (new XPowYNode (this, token, make_precedence_t (token.type), get_associativity (token)));
+
+ if (!expression_1 ())
+ return false;
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.SUP_NUMBER)
+ {
+ insert_into_tree (new XPowYIntegerNode (this, null, make_precedence_p (Precedence.POWER), get_associativity_p (Precedence.POWER)));
+ insert_into_tree (new NameNode (this, token, make_precedence_p (Precedence.NUMBER_VARIABLE), get_associativity_p (Precedence.NUMBER_VARIABLE)));
+
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.NSUP_NUMBER)
+ {
+ insert_into_tree (new XPowYIntegerNode (this, null, make_precedence_p (Precedence.POWER), get_associativity_p (Precedence.POWER)));
+ insert_into_tree (new NameNode (this, token, make_precedence_p (Precedence.NUMBER_VARIABLE), get_associativity_p (Precedence.NUMBER_VARIABLE)));
+
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.FACTORIAL)
+ {
+ insert_into_tree_unary (new FactorialNode (this, token, make_precedence_t (token.type), get_associativity (token)));
+
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.MULTIPLY)
+ {
+ insert_into_tree (new MultiplyNode (this, token, make_precedence_t (token.type), get_associativity (token)));
+
+ if (!expression_1 ())
+ return false;
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.PERCENTAGE)
+ {
+ insert_into_tree_unary (new PercentNode (this, token, make_precedence_t (token.type), get_associativity (token)));
+
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.AND)
+ {
+ insert_into_tree (new AndNode (this, token, make_precedence_t (token.type), get_associativity (token)));
+
+ if (!expression_1 ())
+ return false;
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.OR)
+ {
+ insert_into_tree (new OrNode (this, token, make_precedence_t (token.type), get_associativity (token)));
+
+ if (!expression_1 ())
+ return false;
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.XOR)
+ {
+ insert_into_tree (new XorNode (this, token, make_precedence_t (token.type), get_associativity (token)));
+
+ if (!expression_1 ())
+ return false;
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.DIVIDE)
+ {
+ insert_into_tree (new DivideNode (this, token, make_precedence_t (token.type), get_associativity (token)));
+
+ if (!expression_1 ())
+ return false;
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.MOD)
+ {
+ insert_into_tree (new ModulusDivideNode (this, token, make_precedence_t (token.type), get_associativity (token)));
+
+ if (!expression_1 ())
+ return false;
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.ADD)
+ {
+ var node = new AddNode (this, token, make_precedence_t (token.type), get_associativity (token));
+ insert_into_tree (node);
+
+ if (!expression_1 ())
+ return false;
+
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.PERCENTAGE)
+ {
+ //FIXME: This condition needs to be verified for all cases.. :(
+ if (node.right.precedence > Precedence.PERCENTAGE)
+ {
+ node.precedence = Precedence.PERCENTAGE;
+ node.do_percentage = true;
+ return true;
+ }
+ else
+ {
+ /* Assume '%' to be part of 'expression PERCENTAGE' statement. */
+ lexer.roll_back ();
+ if (!expression_2 ())
+ return true;
+ }
+ }
+ else
+ lexer.roll_back ();
+
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.SUBTRACT)
+ {
+ var node = new SubtractNode (this, token, make_precedence_t (token.type), get_associativity (token));
+ insert_into_tree (node);
+
+ if (!expression_1 ())
+ return false;
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.PERCENTAGE)
+ {
+ //FIXME: This condition needs to be verified for all cases.. :(
+ if (node.right.precedence > Precedence.PERCENTAGE)
+ {
+ node.precedence = Precedence.PERCENTAGE;
+ node.do_percentage = true;
+ return true;
+ }
+ else
+ {
+ /* Assume '%' to be part of 'expression PERCENTAGE' statement. */
+ lexer.roll_back ();
+ if (!expression_2 ())
+ return true;
+ }
+ }
+ else
+ lexer.roll_back ();
+
+ if (!expression_2 ())
+ return false;
+
+ return true;
+ }
+ else
+ {
+ lexer.roll_back ();
+ return true;
+ }
+ }
+
+ private bool variable ()
+ {
+ var token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.FUNCTION)
+ {
+ var token_old = token;
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.SUP_NUMBER)
+ {
+ /* Pass power as void * value. That will be taken care in pf_apply_func_with_power. */
+
+ insert_into_tree_unary (new FunctionWithPowerNode (this, token_old, make_precedence_t (token_old.type), get_associativity (token_old), token.text));
+ if (!expression ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.NSUP_NUMBER)
+ {
+ /* Pass power as void * value. That will be taken care in pf_apply_func_with_npower. */
+
+ insert_into_tree_unary (new FunctionWithNegativePowerNode (this, token_old, make_precedence_t (token_old.type), get_associativity (token_old), token.text));
+ if (!expression ())
+ return false;
+
+ return true;
+ }
+ else
+ {
+ lexer.roll_back ();
+ insert_into_tree_unary (new FunctionNode (this, token_old, make_precedence_t (token_old.type), get_associativity (token_old)));
+ if (!expression ())
+ return false;
+
+ return true;
+ }
+ }
+ else if (token.type == LexerTokenType.SUB_NUMBER)
+ {
+ var token_old = token;
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.ROOT)
+ {
+ insert_into_tree_unary (new RootNode (this, token, make_precedence_t (token.type), get_associativity (token), sub_atoi (token_old.text)));
+ if (!expression ())
+ return false;
+
+ return true;
+ }
+ else
+ return false;
+ }
+ else if (token.type == LexerTokenType.ROOT)
+ {
+ insert_into_tree_unary (new RootNode (this, token, make_precedence_t (token.type), get_associativity (token), 2));
+
+ if (!expression ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.ROOT_3)
+ {
+ insert_into_tree_unary (new RootNode (this, token, make_precedence_t (token.type), get_associativity (token), 3));
+
+ if (!expression ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.ROOT_4)
+ {
+ insert_into_tree_unary (new RootNode (this, token, make_precedence_t (token.type), get_associativity (token), 4));
+
+ if (!expression ())
+ return false;
+
+ return true;
+ }
+ else if (token.type == LexerTokenType.VARIABLE)
+ {
+ lexer.roll_back ();
+ //TODO: unknown function ERROR for (VARIABLE SUP_NUMBER expression).
+ if (!term ())
+ return false;
+
+ return true;
+ }
+ else
+ return false;
+ }
+
+ private bool term ()
+ {
+ var token = lexer.get_next_token ();
+
+ if (token.type == LexerTokenType.VARIABLE)
+ {
+ var token_old = token;
+ /* Check if the token is a valid variable or not. */
+ if (!check_variable (token.text))
+ {
+ set_error (ErrorCode.UNKNOWN_VARIABLE, token.text);
+ return false;
+ }
+ token = lexer.get_next_token ();
+ if (token.type == LexerTokenType.SUP_NUMBER)
+ insert_into_tree (new VariableWithPowerNode (this, token_old, make_precedence_t (token_old.type), get_associativity (token_old), token.text));
+ else
+ {
+ lexer.roll_back ();
+ insert_into_tree (new VariableNode (this, token_old, make_precedence_t (token_old.type), get_associativity (token_old)));
+ }
+
+ if (!term_2 ())
+ return false;
+
+ return true;
+ }
+ else
+ return false;
+ }
+
+ private bool term_2 ()
+ {
+ var token = lexer.get_next_token ();
+ lexer.roll_back ();
+
+ if (token.type == LexerTokenType.PL_EOS || token.type == LexerTokenType.ASSIGN)
+ return true;
+
+ if (token.type == LexerTokenType.VARIABLE)
+ {
+ /* Insert multiply in between two distinct (variable). */
+ insert_into_tree (new MultiplyNode (this, null, make_precedence_p (Precedence.MULTIPLY), get_associativity_p (Precedence.MULTIPLY)));
+
+ if (!term ())
+ return false;
+
+ return true;
+ }
+ else
+ return true;
+ }
+}
diff --git a/src/equation.vala b/src/equation.vala
new file mode 100644
index 0000000..fa327ea
--- /dev/null
+++ b/src/equation.vala
@@ -0,0 +1,323 @@
+/*
+ * Copyright (C) 2004-2008 Sami Pietila
+ * Copyright (C) 2008-2012 Robert Ancell.
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public int sub_atoi (string data)
+{
+ const unichar digits[] = {'â', 'â', 'â', 'â', 'â', 'â', 'â', 'â', 'â', 'â'};
+
+ var index = 0;
+ unichar c;
+ int value = 0;
+ while (data.get_next_char (ref index, out c))
+ {
+ var is_subdigit = false;
+ for (var i = 0; i < digits.length; i++)
+ {
+ if (c == digits[i])
+ {
+ value = value * 10 + i;
+ is_subdigit = true;
+ break;
+ }
+ }
+ if (!is_subdigit)
+ return -1;
+ }
+
+ return value;
+}
+
+public int super_atoi (string data)
+{
+ const unichar digits[] = {'â', 'Â', 'Â', 'Â', 'â', 'â', 'â', 'â', 'â', 'â'};
+
+ var index = 0;
+ unichar c;
+ data.get_next_char (ref index, out c);
+ int sign = 1;
+ if (c == 'â')
+ sign = -1;
+ else
+ index = 0;
+
+ int value = 0;
+ while (data.get_next_char (ref index, out c))
+ {
+ var is_superdigit = false;
+ for (var i = 0; i < digits.length; i++)
+ {
+ if (c == digits[i])
+ {
+ value = value * 10 + i;
+ is_superdigit = true;
+ break;
+ }
+ }
+ if (!is_superdigit)
+ return 0;
+ }
+
+ return sign * value;
+}
+
+string mp_error_code_to_string (ErrorCode error_code)
+{
+ switch (error_code)
+ {
+ case ErrorCode.NONE:
+ return "ErrorCode.NONE";
+ case ErrorCode.INVALID:
+ return "ErrorCode.INVALID";
+ case ErrorCode.OVERFLOW:
+ return "ErrorCode.OVERFLOW";
+ case ErrorCode.UNKNOWN_VARIABLE:
+ return "ErrorCode.UNKNOWN_VARIABLE";
+ case ErrorCode.UNKNOWN_FUNCTION:
+ return "ErrorCode.UNKNOWN_FUNCTION";
+ case ErrorCode.UNKNOWN_CONVERSION:
+ return "ErrorCode.UNKNOWN_CONVERSION";
+ case ErrorCode.MP:
+ return "ErrorCode.MP";
+ default:
+ return "Unknown parser error";
+ }
+}
+
+public enum ErrorCode
+{
+ NONE,
+ INVALID,
+ OVERFLOW,
+ UNKNOWN_VARIABLE,
+ UNKNOWN_FUNCTION,
+ UNKNOWN_CONVERSION,
+ MP
+}
+
+public class Equation
+{
+ public int base;
+ public int wordlen;
+ public AngleUnit angle_units;
+ private string expression;
+
+ public Equation (string expression)
+ {
+ this.expression = expression;
+ }
+
+ public new Number? parse (out ErrorCode error_code = null, out string error_token = null)
+ {
+ var parser = new EquationParser (this, expression);
+ mp_clear_error ();
+
+ var z = parser.parse (out error_code, out error_token);
+
+ /* Error during parsing */
+ if (error_code != ErrorCode.NONE)
+ return null;
+
+ if (mp_get_error () != null)
+ {
+ error_code = ErrorCode.MP;
+ return null;
+ }
+
+ return z;
+ }
+
+ public virtual bool variable_is_defined (string name)
+ {
+ return false;
+ }
+
+ public virtual Number? get_variable (string name)
+ {
+ return null;
+ }
+
+ public virtual void set_variable (string name, Number x)
+ {
+ }
+
+ public virtual bool function_is_defined (string name)
+ {
+ return false;
+ }
+
+ public virtual Number? get_function (string name, Number x)
+ {
+ return null;
+ }
+
+ public virtual Number? convert (Number x, string x_units, string z_units)
+ {
+ return null;
+ }
+}
+
+private class EquationParser : Parser
+{
+ private Equation equation;
+
+ public EquationParser (Equation equation, string expression)
+ {
+ base (expression, equation.base, equation.wordlen);
+ this.equation = equation;
+ }
+
+ protected override bool variable_is_defined (string name)
+ {
+ /* FIXME: Make more generic */
+ if (name == "e" || name == "i" || name == "Ï")
+ return true;
+
+ return equation.variable_is_defined (name);
+ }
+
+ protected override Number? get_variable (string name)
+ {
+ if (name == "e")
+ return new Number.eulers ();
+ else if (name == "i")
+ return new Number.i ();
+ else if (name == "Ï")
+ return new Number.pi ();
+ else
+ return equation.get_variable (name);
+ }
+
+ protected override void set_variable (string name, Number x)
+ {
+ // Reserved words, e, Ï, mod, and, or, xor, not, abs, log, ln, sqrt, int, frac, sin, cos, ...
+ if (name == "e" || name == "i" || name == "Ï")
+ return; // FALSE
+
+ equation.set_variable (name, x);
+ }
+
+ // FIXME: Accept "2sin" not "2 sin", i.e. let the tokenizer collect the multiple
+ // Parser then distinguishes between "sin"="s*i*n" or "sin5" = "sin 5" = "sin (5)"
+ // i.e. numbers+letters = variable or function depending on following arg
+ // letters+numbers = numbers+letters+numbers = function
+
+ protected override bool function_is_defined (string name)
+ {
+ var lower_name = name.down ();
+
+ /* FIXME: Make more generic */
+ if (lower_name == "log" ||
+ (lower_name.has_prefix ("log") && sub_atoi (lower_name.substring (3)) >= 0) ||
+ lower_name == "ln" ||
+ lower_name == "sqrt" ||
+ lower_name == "abs" ||
+ lower_name == "sgn" ||
+ lower_name == "arg" ||
+ lower_name == "conj" ||
+ lower_name == "int" ||
+ lower_name == "frac" ||
+ lower_name == "floor" ||
+ lower_name == "ceil" ||
+ lower_name == "round" ||
+ lower_name == "re" ||
+ lower_name == "im" ||
+ lower_name == "sin" || lower_name == "cos" || lower_name == "tan" ||
+ lower_name == "asin" || lower_name == "acos" || lower_name == "atan" ||
+ lower_name == "sinâÂ" || lower_name == "cosâÂ" || lower_name == "tanâÂ" ||
+ lower_name == "sinh" || lower_name == "cosh" || lower_name == "tanh" ||
+ lower_name == "sinhâÂ" || lower_name == "coshâÂ" || lower_name == "tanhâÂ" ||
+ lower_name == "asinh" || lower_name == "acosh" || lower_name == "atanh" ||
+ lower_name == "ones" ||
+ lower_name == "twos")
+ return true;
+
+ return equation.function_is_defined (name);
+ }
+
+ protected override Number? get_function (string name, Number x)
+ {
+ var lower_name = name.down ();
+
+ // FIXME: Re Im ?
+
+ if (lower_name == "log")
+ return x.logarithm (10); // FIXME: Default to ln
+ else if (lower_name.has_prefix ("log"))
+ {
+ var number_base = sub_atoi (lower_name.substring (3));
+ if (number_base < 0)
+ return null;
+ else
+ return x.logarithm (number_base);
+ }
+ else if (lower_name == "ln")
+ return x.ln ();
+ else if (lower_name == "sqrt") // âx
+ return x.sqrt ();
+ else if (lower_name == "abs") // |x|
+ return x.abs ();
+ else if (lower_name == "sgn")
+ return x.sgn ();
+ else if (lower_name == "arg")
+ return x.arg (equation.angle_units);
+ else if (lower_name == "conj")
+ return x.conjugate ();
+ else if (lower_name == "int")
+ return x.integer_component ();
+ else if (lower_name == "frac")
+ return x.fractional_component ();
+ else if (lower_name == "floor")
+ return x.floor ();
+ else if (lower_name == "ceil")
+ return x.ceiling ();
+ else if (lower_name == "round")
+ return x.round ();
+ else if (lower_name == "re")
+ return x.real_component ();
+ else if (lower_name == "im")
+ return x.imaginary_component ();
+ else if (lower_name == "sin")
+ return x.sin (equation.angle_units);
+ else if (lower_name == "cos")
+ return x.cos (equation.angle_units);
+ else if (lower_name == "tan")
+ return x.tan (equation.angle_units);
+ else if (lower_name == "sinâÂ" || lower_name == "asin")
+ return x.asin (equation.angle_units);
+ else if (lower_name == "cosâÂ" || lower_name == "acos")
+ return x.acos (equation.angle_units);
+ else if (lower_name == "tanâÂ" || lower_name == "atan")
+ return x.atan (equation.angle_units);
+ else if (lower_name == "sinh")
+ return x.sinh ();
+ else if (lower_name == "cosh")
+ return x.cosh ();
+ else if (lower_name == "tanh")
+ return x.tanh ();
+ else if (lower_name == "sinhâÂ" || lower_name == "asinh")
+ return x.asinh ();
+ else if (lower_name == "coshâÂ" || lower_name == "acosh")
+ return x.acosh ();
+ else if (lower_name == "tanhâÂ" || lower_name == "atanh")
+ return x.atanh ();
+ else if (lower_name == "ones")
+ return x.ones_complement (equation.wordlen);
+ else if (lower_name == "twos")
+ return x.twos_complement (equation.wordlen);
+ else
+ return equation.get_function (name, x);
+ }
+
+ protected override Number? convert (Number x, string x_units, string z_units)
+ {
+ return equation.convert (x, x_units, z_units);
+ }
+}
diff --git a/src/financial.vala b/src/financial.vala
new file mode 100644
index 0000000..c7c758f
--- /dev/null
+++ b/src/financial.vala
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (C) 2008-2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public enum FinancialDialog
+{
+ CTRM_DIALOG,
+ DDB_DIALOG,
+ FV_DIALOG,
+ GPM_DIALOG,
+ PMT_DIALOG,
+ PV_DIALOG,
+ RATE_DIALOG,
+ SLN_DIALOG,
+ SYD_DIALOG,
+ TERM_DIALOG
+}
+
+public void do_finc_expression (MathEquation equation, FinancialDialog function, Number arg1, Number arg2, Number arg3, Number arg4)
+{
+ Number result;
+ switch (function)
+ {
+ case FinancialDialog.CTRM_DIALOG:
+ result = calc_ctrm (equation, arg1, arg2, arg3);
+ break;
+ case FinancialDialog.DDB_DIALOG:
+ result = calc_ddb (equation, arg1, arg2, arg3);
+ break;
+ case FinancialDialog.FV_DIALOG:
+ result = calc_fv (equation, arg1, arg2, arg3);
+ break;
+ case FinancialDialog.GPM_DIALOG:
+ result = calc_gpm (equation, arg1, arg2);
+ break;
+ case FinancialDialog.PMT_DIALOG:
+ result = calc_pmt (equation, arg1, arg2, arg3);
+ break;
+ case FinancialDialog.PV_DIALOG:
+ result = calc_pv (equation, arg1, arg2, arg3);
+ break;
+ case FinancialDialog.RATE_DIALOG:
+ result = calc_rate (equation, arg1, arg2, arg3);
+ break;
+ case FinancialDialog.SLN_DIALOG:
+ result = calc_sln (equation, arg1, arg2, arg3);
+ break;
+ case FinancialDialog.SYD_DIALOG:
+ result = calc_syd (equation, arg1, arg2, arg3, arg4);
+ break;
+ case FinancialDialog.TERM_DIALOG:
+ result = calc_term (equation, arg1, arg2, arg3);
+ break;
+ default:
+ result = new Number.integer (0);
+ break;
+ }
+
+ equation.set_number (result);
+}
+
+private Number calc_ctrm (MathEquation equation, Number pint, Number fv, Number pv)
+{
+ /* Cterm - pint (periodic interest rate).
+ * fv (future value).
+ * pv (present value).
+ *
+ * RESULT = log (fv / pv) / log (1 + pint)
+ */
+ var t1 = fv.divide (pv);
+ var t2 = t1.ln ();
+ var t3 = pint.add (new Number.integer (1));
+ var t4 = t3.ln ();
+ return t2.divide (t4);
+}
+
+private Number calc_ddb (MathEquation equation, Number cost, Number life, Number period)
+{
+ /* Ddb - cost (amount paid for asset).
+ * life (useful life of the asset).
+ * period (time period for depreciation allowance).
+ *
+ * bv = 0.0;
+ * for (i = 0; i < life; i++)
+ * {
+ * VAL = ((cost - bv) * 2) / life
+ * bv += VAL
+ * }
+ * RESULT = VAL
+ *
+ */
+
+ var z = new Number.integer (0);
+
+ var tbv = new Number.integer (0);
+ var len = period.to_integer ();
+ for (var i = 0; i < len; i++)
+ {
+ var t1 = cost.subtract (tbv);
+ var t2 = t1.multiply_integer (2);
+ z = t2.divide (life);
+ t1 = tbv;
+ tbv = t1.add (z); /* TODO: why result is tbv, for next loop? */
+ }
+
+ if (len >= 0)
+ equation.status = _("Error: the number of periods must be positive");
+
+ return z;
+}
+
+private Number calc_fv (MathEquation equation, Number pmt, Number pint, Number n)
+{
+ /* Fv - pmt (periodic payment).
+ * pint (periodic interest rate).
+ * n (number of periods).
+ *
+ * RESULT = pmt * (pow (1 + pint, n) - 1) / pint
+ */
+
+ var t1 = pint.add (new Number.integer (1));
+ var t2 = t1.xpowy (n);
+ var t3 = t2.add (new Number.integer (-1));
+ var t4 = pmt.multiply (t3);
+ return t4.divide (pint);
+}
+
+private Number calc_gpm (MathEquation equation, Number cost, Number margin)
+{
+ /* Gpm - cost (cost of sale).
+ * margin (gross profit margin.
+ *
+ * RESULT = cost / (1 - margin)
+ */
+
+ var t1 = new Number.integer (1);
+ var t2 = t1.subtract (margin);
+ return cost.divide (t2);
+}
+
+private Number calc_pmt (MathEquation equation, Number prin, Number pint, Number n)
+{
+ /* Pmt - prin (principal).
+ * pint (periodic interest rate).
+ * n (term).
+ *
+ * RESULT = prin * (pint / (1 - pow (pint + 1, -1 * n)))
+ */
+
+ var t1 = pint.add (new Number.integer (1));
+ var t2 = n.multiply_integer (-1);
+ var t3 = t1.xpowy (t2);
+ var t4 = t3.multiply_integer (-1);
+ t1 = t4.add (new Number.integer (1));
+ t2 = pint.divide (t1);
+ return prin.multiply (t2);
+}
+
+private Number calc_pv (MathEquation equation, Number pmt, Number pint, Number n)
+{
+ /* Pv - pmt (periodic payment).
+ * pint (periodic interest rate).
+ * n (term).
+ *
+ * RESULT = pmt * (1 - pow (1 + pint, -1 * n)) / pint
+ */
+
+ var t1 = pint.add (new Number.integer (1));
+ var t2 = n.multiply_integer (-1);
+ var t3 = t1.xpowy (t2);
+ var t4 = t3.multiply_integer (-1);
+ t1 = t4.add (new Number.integer (1));
+ t2 = t1.divide (pint);
+ return pmt.multiply (t2);
+}
+
+private Number calc_rate (MathEquation equation, Number fv, Number pv, Number n)
+{
+ /* Rate - fv (future value).
+ * pv (present value).
+ * n (term).
+ *
+ * RESULT = pow (fv / pv, 1 / n) - 1
+ */
+
+ var t1 = fv.divide (pv);
+ var t2 = new Number.integer (1);
+ var t3 = t2.divide (n);
+ var t4 = t1.xpowy (t3);
+ return t4.add (new Number.integer (-1));
+}
+
+private Number calc_sln (MathEquation equation, Number cost, Number salvage, Number life)
+{
+ /* Sln - cost (cost of the asset).
+ * salvage (salvage value of the asset).
+ * life (useful life of the asset).
+ *
+ * RESULT = (cost - salvage) / life
+ */
+
+ var t1 = cost.subtract (salvage);
+ return t1.divide (life);
+}
+
+private Number calc_syd (MathEquation equation, Number cost, Number salvage, Number life, Number period)
+{
+ /* Syd - cost (cost of the asset).
+ * salvage (salvage value of the asset).
+ * life (useful life of the asset).
+ * period (period for which depreciation is computed).
+ *
+ * RESULT = (cost - salvage) * (life - period + 1) /
+ * (life * (life + 1)) / 2
+ */
+
+ var t3 = life.subtract (period).add (new Number.integer (1));
+ var t2 = life.add (new Number.integer (1));
+ var t4 = life.multiply (t2);
+ var t1 = t4.divide (new Number.integer (2));
+ t2 = t3.divide (t1);
+ t1 = cost.subtract (salvage);
+ return t1.multiply (t2);
+}
+
+private Number calc_term (MathEquation equation, Number pmt, Number fv, Number pint)
+{
+ /* Term - pmt (periodic payment).
+ * fv (future value).
+ * pint (periodic interest rate).
+ *
+ * RESULT = log (1 + (fv * pint / pmt)) / log (1 + pint)
+ */
+
+ var t1 = pint.add (new Number.integer (1));
+ var t2 = t1.ln ();
+ t1 = fv.multiply (pint);
+ var t3 = t1.divide (pmt);
+ var t4 = t3.add (new Number.integer (1));
+ t1 = t4.ln ();
+ return t1.divide (t2);
+}
diff --git a/src/gcalccmd.vala b/src/gcalccmd.vala
new file mode 100644
index 0000000..bc5e7a0
--- /dev/null
+++ b/src/gcalccmd.vala
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2009 Rich Burridge
+ * Copyright (C) 2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+private const int MAXLINE = 1024;
+
+private static Serializer result_serializer;
+
+static void solve (string equation)
+{
+ var e = new Equation (equation);
+ e.base = 10;
+ e.wordlen = 32;
+ e.angle_units = AngleUnit.DEGREES;
+
+ ErrorCode ret;
+ var z = e.parse (out ret, null);
+
+ if (z != null)
+ stdout.printf ("%s\n", result_serializer.to_string (z));
+ else if (ret == ErrorCode.MP)
+ stderr.printf ("Error %s\n", mp_get_error ());
+ else
+ stderr.printf ("Error %d\n", ret);
+}
+
+public static int main (string[] args)
+{
+ /* Seed random number generator. */
+ var now = new DateTime.now_utc ();
+ Random.set_seed (now.get_microsecond ());
+
+ Intl.setlocale (LocaleCategory.ALL, "");
+
+ result_serializer = new Serializer (DisplayFormat.AUTOMATIC, 10, 9);
+
+ var buffer = new char[1024];
+ while (true)
+ {
+ stdout.printf ("> ");
+ var line = stdin.gets (buffer);
+
+ line = line.strip ();
+ if (line == null || line == "exit" || line == "quit" || line == "")
+ break;
+
+ solve (line);
+ }
+
+ return Posix.EXIT_SUCCESS;
+}
diff --git a/src/gcalctool.vala b/src/gcalctool.vala
new file mode 100644
index 0000000..5484f51
--- /dev/null
+++ b/src/gcalctool.vala
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (C) 2008-2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public class GCalctool : Gtk.Application
+{
+ private Settings settings;
+ private MathWindow window;
+ private MathPreferencesDialog preferences_dialog;
+
+ private const ActionEntry[] app_entries =
+ {
+ { "copy", copy_cb, null, null, null },
+ { "paste", paste_cb, null, null, null },
+ { "undo", undo_cb, null, null, null },
+ { "redo", redo_cb, null, null, null },
+ { "mode", mode_changed_cb, "s", "\"basic\"", null },
+ { "preferences", show_preferences_cb, null, null, null },
+ { "help", help_cb, null, null, null },
+ { "about", about_cb, null, null, null },
+ { "quit", quit_cb, null, null, null },
+ };
+
+ public GCalctool ()
+ {
+ Object (flags : ApplicationFlags.NON_UNIQUE);
+ }
+
+ protected override void startup ()
+ {
+ base.startup ();
+
+ settings = new Settings ("org.gnome.gcalctool");
+ var accuracy = settings.get_int ("accuracy");
+ var word_size = settings.get_int ("word-size");
+ var number_base = settings.get_int ("base");
+ var show_tsep = settings.get_boolean ("show-thousands");
+ var show_zeroes = settings.get_boolean ("show-zeroes");
+ var number_format = (DisplayFormat) settings.get_enum ("number-format");
+ var angle_units = (AngleUnit) settings.get_enum ("angle-units");
+ var button_mode = (ButtonMode) settings.get_enum ("button-mode");
+ var source_currency = settings.get_string ("source-currency");
+ var target_currency = settings.get_string ("target-currency");
+ var source_units = settings.get_string ("source-units");
+ var target_units = settings.get_string ("target-units");
+
+ var equation = new MathEquation ();
+ equation.set_accuracy (accuracy);
+ equation.word_size = word_size;
+ equation.set_show_thousands_separators (show_tsep);
+ equation.set_show_trailing_zeroes (show_zeroes);
+ equation.set_number_format (number_format);
+ equation.set_angle_units (angle_units);
+ equation.source_currency = source_currency;
+ equation.target_currency = target_currency;
+ equation.source_units = source_units;
+ equation.target_units = target_units;
+
+ add_action_entries (app_entries, this);
+
+ window = new MathWindow (this, equation);
+ var buttons = window.buttons;
+ buttons.set_programming_base (number_base);
+ buttons.mode = button_mode; // FIXME: We load the basic buttons even if we immediately switch to the next type
+ buttons.notify["mode"].connect ((pspec) => { mode_cb (); });
+ mode_cb ();
+
+ var menu = new Menu ();
+
+ var section = new Menu ();
+ section.append (_("Basic"), "app.mode::basic");
+ section.append (_("Advanced"), "app.mode::advanced");
+ section.append (_("Financial"), "app.mode::financial");
+ section.append (_("Programming"), "app.mode::programming");
+ menu.append_section (_("Mode"), section);
+
+ section = new Menu ();
+ section.append (_("Preferences"), "app.preferences");
+ menu.append_section (null, section);
+
+ section = new Menu ();
+ section.append (_("About Calculator"), "app.about");
+ section.append (_("Help"), "app.help");
+ section.append (_("Quit"), "app.quit");
+ menu.append_section (null, section);
+
+ set_app_menu (menu);
+
+ add_accelerator ("<control>Q", "app.quit", null);
+ add_accelerator ("F1", "app.help", null);
+ add_accelerator ("<control>C", "app.copy", null);
+ add_accelerator ("<control>V", "app.paste", null);
+ add_accelerator ("<control>Z", "app.undo", null);
+ add_accelerator ("<control><shift>Z", "app.redo", null);
+ }
+
+ protected override void activate ()
+ {
+ base.activate ();
+
+ window.present ();
+ }
+
+ protected override void shutdown ()
+ {
+ base.shutdown ();
+
+ var equation = window.equation;
+ var buttons = window.buttons;
+
+ settings.set_enum ("button-mode", buttons.mode);
+ settings.set_int ("accuracy", equation.get_accuracy ());
+ settings.set_int ("word-size", equation.word_size);
+ settings.set_boolean ("show-thousands", equation.get_show_thousands_separators ());
+ settings.set_boolean ("show-zeroes", equation.get_show_trailing_zeroes ());
+ settings.set_enum ("number-format", equation.get_number_format ());
+ settings.set_enum ("angle-units", equation.get_angle_units ());
+ settings.set_string ("source-currency", equation.source_currency);
+ settings.set_string ("target-currency", equation.target_currency);
+ settings.set_string ("source-units", equation.source_units);
+ settings.set_string ("target-units", equation.target_units);
+ settings.set_int ("base", buttons.get_programming_base ());
+ }
+
+ private static void solve (string equation)
+ {
+ var e = new SolveEquation (equation);
+ e.base = 10;
+ e.wordlen = 32;
+ e.angle_units = AngleUnit.DEGREES;
+
+ ErrorCode error;
+ var result = e.parse (out error);
+ if (result != null)
+ {
+ var serializer = new Serializer (DisplayFormat.AUTOMATIC, 10, 9);
+ stdout.printf ("%s\n", serializer.to_string (result));
+ Posix.exit (0);
+ }
+ else if (error == ErrorCode.MP)
+ {
+ stderr.printf ("Error: %s\n", mp_get_error ());
+ Posix.exit (1);
+ }
+ else
+ {
+ stderr.printf ("Error: %s\n", mp_error_code_to_string (error));
+ Posix.exit (1);
+ }
+ }
+
+ private static void usage (string progname, bool show_application, bool show_gtk)
+ {
+ stderr.printf (/* Description on how to use gcalctool displayed on command-line */
+ _("Usage:\n %s â Perform mathematical calculations"), progname);
+
+ stderr.printf ("\n\n");
+
+ stderr.printf (/* Description on gcalctool command-line help options displayed on command-line */
+ _("Help Options:\n -v, --version Show release version\n -h, -?, --help Show help options\n --help-all Show all help options\n --help-gtk Show GTK+ options"));
+ stderr.printf ("\n\n");
+
+ if (show_gtk)
+ {
+ stderr.printf (/* Description on gcalctool command-line GTK+ options displayed on command-line */
+ _("GTK+ Options:\n --class=CLASS Program class as used by the window manager\n --name=NAME Program name as used by the window manager\n --screen=SCREEN X screen to use\n --sync Make X calls synchronous\n --gtk-module=MODULES Load additional GTK+ modules\n --g-fatal-warnings Make all warnings fatal"));
+ stderr.printf ("\n\n");
+ }
+
+ if (show_application)
+ {
+ stderr.printf (/* Description on gcalctool application options displayed on command-line */
+ _("Application Options:\n -s, --solve <equation> Solve the given equation"));
+ stderr.printf ("\n\n");
+ }
+ }
+
+ private static void get_options (string[] args)
+ {
+ var progname = Path.get_basename (args[0]);
+
+ for (var i = 1; i < args.length; i++)
+ {
+ var arg = args[i];
+
+ if (arg == "-v" || arg == "--version")
+ {
+ /* NOTE: Is not translated so can be easily parsed */
+ stderr.printf ("%1$s %2$s\n", progname, VERSION);
+ Posix.exit (0);
+ }
+ else if (arg == "-h" || arg == "-?" || arg == "--help")
+ {
+ usage (progname, true, false);
+ Posix.exit (0);
+ }
+ else if (arg == "--help-all")
+ {
+ usage (progname, true, true);
+ Posix.exit (0);
+ }
+ else if (arg == "--help-gtk")
+ {
+ usage (progname, false, true);
+ Posix.exit (0);
+ }
+ else if (arg == "-s" || arg == "--solve")
+ {
+ i++;
+ if (i >= args.length)
+ {
+ stderr.printf (/* Error printed to stderr when user uses --solve argument without an equation */
+ _("Argument --solve requires an equation to solve"));
+ stderr.printf ("\n");
+ Posix.exit (1);
+ }
+ else
+ solve (args[i]);
+ }
+ else
+ {
+ stderr.printf (/* Error printed to stderr when user provides an unknown command-line argument */
+ _("Unknown argument '%s'"), arg);
+ stderr.printf ("\n");
+ usage (progname, true, false);
+ Posix.exit (1);
+ }
+ }
+ }
+
+ private void mode_cb ()
+ {
+ var buttons = window.buttons;
+ var state = "basic";
+ switch (buttons.mode)
+ {
+ default:
+ case ButtonMode.BASIC:
+ state = "basic";
+ //FIXME: Should it revert to decimal mode? equation.set_number_format (window->priv->equation, DEC);
+ break;
+
+ case ButtonMode.ADVANCED:
+ state = "advanced";
+ break;
+
+ case ButtonMode.FINANCIAL:
+ state = "financial";
+ break;
+
+ case ButtonMode.PROGRAMMING:
+ state = "programming";
+ break;
+ }
+
+ var action = lookup_action ("mode") as SimpleAction;
+ action.set_state (new Variant.string (state));
+ }
+
+ private void copy_cb ()
+ {
+ window.equation.copy ();
+ }
+
+ private void paste_cb ()
+ {
+ window.equation.paste ();
+ }
+
+ private void undo_cb ()
+ {
+ window.equation.undo ();
+ }
+
+ private void redo_cb ()
+ {
+ window.equation.redo ();
+ }
+
+ private void mode_changed_cb (SimpleAction action, Variant? parameter)
+ {
+ var mode = ButtonMode.BASIC;
+ var mode_str = parameter.get_string (null);
+ if (mode_str == "basic")
+ mode = ButtonMode.BASIC;
+ else if (mode_str == "advanced")
+ mode = ButtonMode.ADVANCED;
+ else if (mode_str == "financial")
+ mode = ButtonMode.FINANCIAL;
+ else if (mode_str == "programming")
+ mode = ButtonMode.PROGRAMMING;
+ window.buttons.mode = mode;
+ }
+
+ private void show_preferences_cb ()
+ {
+ if (preferences_dialog == null)
+ {
+ preferences_dialog = new MathPreferencesDialog (window.equation);
+ preferences_dialog.set_transient_for (window);
+ }
+ preferences_dialog.present ();
+ }
+
+ private void help_cb ()
+ {
+ try
+ {
+ Gtk.show_uri (window.get_screen (), "help:gcalctool", Gtk.get_current_event_time ());
+ }
+ catch (Error e)
+ {
+ /* Translators: Error message displayed when unable to launch help browser */
+ var message = _("Unable to open help file");
+
+ var d = new Gtk.MessageDialog (window,
+ Gtk.DialogFlags.MODAL | Gtk.DialogFlags.DESTROY_WITH_PARENT,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.CLOSE,
+ "%s", message);
+ d.format_secondary_text ("%s", e.message);
+ d.run ();
+ d.destroy ();
+ }
+ }
+
+ private void about_cb ()
+ {
+ string[] authors =
+ {
+ "Rich Burridge <rich burridge gmail com>",
+ "Robert Ancell <robert ancell gmail com>",
+ "Klaus NiederkrÃger <kniederk umpa ens-lyon fr>",
+ "Robin Sonefors <ozamosi flukkost nu>",
+ null
+ };
+ string[] documenters =
+ {
+ "Sun Microsystems",
+ null
+ };
+
+ /* The translator credits. Please translate this with your name (s). */
+ var translator_credits = _("translator-credits");
+
+ /* The license this software is under (GPL2+) */
+ var license = _("Gcalctool is free software; you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation; either version 2 of the License, or\n(at your option) any later version.\n\nGcalctool is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with Gcalctool; if not, write to the Free Software Foundation, Inc.,\n51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA");
+
+ Gtk.show_about_dialog (window,
+ "name",
+ /* Program name in the about dialog */
+ _("Gcalctool"),
+ "version", VERSION,
+ "copyright",
+ /* Copyright notice in the about dialog */
+ _("\xc2\xa9 1986â2010 The Gcalctool authors"),
+ "license", license,
+ "comments",
+ /* Short description in the about dialog */
+ _("Calculator with financial and scientific modes."),
+ "authors", authors,
+ "documenters", documenters,
+ "translator_credits", translator_credits,
+ "logo-icon-name", "accessories-calculator");
+ }
+
+ private void quit_cb ()
+ {
+ window.destroy ();
+ }
+
+ public static int main (string[] args)
+ {
+ Intl.setlocale (LocaleCategory.ALL, "");
+ Intl.bindtextdomain (GETTEXT_PACKAGE, LOCALE_DIR);
+ Intl.bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ Intl.textdomain (GETTEXT_PACKAGE);
+
+ /* Seed random number generator. */
+ var now = new DateTime.now_utc ();
+ Random.set_seed (now.get_microsecond ());
+
+ get_options (args);
+
+ Gtk.init (ref args);
+
+ Gtk.Window.set_default_icon_name ("accessories-calculator");
+
+ var app = new GCalctool ();
+
+ return app.run (args);
+ }
+}
+
+private class SolveEquation : Equation
+{
+ public SolveEquation (string text)
+ {
+ base (text);
+ }
+
+ public override Number? convert (Number x, string x_units, string z_units)
+ {
+ return UnitManager.get_default ().convert_by_symbol (x, x_units, z_units);
+ }
+}
\ No newline at end of file
diff --git a/src/math-buttons.vala b/src/math-buttons.vala
new file mode 100644
index 0000000..8dea38d
--- /dev/null
+++ b/src/math-buttons.vala
@@ -0,0 +1,1008 @@
+/*
+ * Copyright (C) 2008-2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public enum ButtonMode
+{
+ BASIC,
+ ADVANCED,
+ FINANCIAL,
+ PROGRAMMING
+}
+
+public class MathButtons : Gtk.Box
+{
+ private MathEquation equation;
+
+ private ButtonMode _mode;
+ public ButtonMode mode
+ {
+ get { return _mode; }
+ set
+ {
+ if (_mode == value)
+ return;
+ _mode = value;
+
+ if (mode == ButtonMode.PROGRAMMING)
+ equation.number_base = programming_base;
+ else
+ equation.number_base = 10;
+
+ load_buttons ();
+
+ converter.set_visible (mode == ButtonMode.ADVANCED || mode == ButtonMode.FINANCIAL);
+ if (mode == ButtonMode.ADVANCED)
+ {
+ converter.set_category (null);
+ converter.set_conversion (equation.source_units, equation.target_units);
+ }
+ else if (mode == ButtonMode.FINANCIAL)
+ {
+ converter.set_category ("currency");
+ converter.set_conversion (equation.source_currency, equation.target_currency);
+ }
+ }
+ }
+ private int programming_base = 10;
+
+ private MathConverter converter;
+
+ private Gtk.Builder basic_ui;
+ private Gtk.Builder advanced_ui;
+ private Gtk.Builder financial_ui;
+ private Gtk.Builder programming_ui;
+
+ private Gtk.Widget bas_panel;
+ private Gtk.Widget adv_panel;
+ private Gtk.Widget fin_panel;
+ private Gtk.Widget prog_panel;
+ private Gtk.Widget? active_panel = null;
+
+ private Gtk.Menu shift_left_menu;
+ private Gtk.Menu shift_right_menu;
+
+ private Gtk.Menu function_menu;
+
+ private List<Gtk.ToggleButton> superscript_toggles;
+ private List<Gtk.ToggleButton> subscript_toggles;
+
+ private Gtk.ComboBox base_combo;
+ private Gtk.Label base_label;
+ private Gtk.Widget bit_panel;
+ private List<Gtk.Label> bit_labels;
+
+ private Gtk.Dialog character_code_dialog;
+ private Gtk.Entry character_code_entry;
+
+ /* The names of each field in the dialogs for the financial functions */
+ // FIXME
+ /*private const string[,] finc_dialog_fields =
+ {
+ {"ctrm_pint", "ctrm_fv", "ctrm_pv", null, null},
+ {"ddb_cost", "ddb_life", "ddb_period", null, null},
+ {"fv_pmt", "fv_pint", "fv_n", null, null},
+ {"gpm_cost", "gpm_margin", null, null, null},
+ {"pmt_prin", "pmt_pint", "pmt_n", null, null},
+ {"pv_pmt", "pv_pint", "pv_n", null, null},
+ {"rate_fv", "rate_pv", "rate_n", null, null},
+ {"sln_cost", "sln_salvage", "sln_life", null, null},
+ {"syd_cost", "syd_salvage", "syd_life", "syd_period", null},
+ {"term_pmt", "term_fv", "term_pint", null, null},
+ {null, null, null, null, null}
+ };*/
+
+ public MathButtons (MathEquation equation)
+ {
+ Object (orientation: Gtk.Orientation.VERTICAL);
+ spacing = 6;
+ show.connect (load_buttons);
+ this.equation = equation;
+
+ equation.notify["display"].connect ((pspec) => { update_bit_panel (); });
+ equation.notify["number-mode"].connect ((pspec) => { number_mode_changed_cb (); });
+ equation.notify["angle-units"].connect ((pspec) => { update_bit_panel (); });
+ equation.notify["number-format"].connect ((pspec) => { update_bit_panel (); });
+ number_mode_changed_cb ();
+ update_bit_panel ();
+ }
+
+ private void load_finc_dialogs ()
+ {
+ var dialog = financial_ui.get_object ("ctrm_dialog") as Gtk.Dialog;
+ dialog.set_data<int> ("finc_dialog", FinancialDialog.CTRM_DIALOG);
+ dialog.response.connect (finc_response_cb);
+ dialog = financial_ui.get_object ("ddb_dialog") as Gtk.Dialog;
+ dialog.set_data<int> ("finc_dialog", FinancialDialog.DDB_DIALOG);
+ dialog.response.connect (finc_response_cb);
+ dialog = financial_ui.get_object ("fv_dialog") as Gtk.Dialog;
+ dialog.set_data<int> ("finc_dialog", FinancialDialog.FV_DIALOG);
+ dialog.response.connect (finc_response_cb);
+ dialog = financial_ui.get_object ("gpm_dialog") as Gtk.Dialog;
+ dialog.set_data<int> ("finc_dialog", FinancialDialog.GPM_DIALOG);
+ dialog.response.connect (finc_response_cb);
+ dialog = financial_ui.get_object ("pmt_dialog") as Gtk.Dialog;
+ dialog.set_data<int> ("finc_dialog", FinancialDialog.PMT_DIALOG);
+ dialog.response.connect (finc_response_cb);
+ dialog = financial_ui.get_object ("pv_dialog") as Gtk.Dialog;
+ dialog.set_data<int> ("finc_dialog", FinancialDialog.PV_DIALOG);
+ dialog.response.connect (finc_response_cb);
+ dialog = financial_ui.get_object ("rate_dialog") as Gtk.Dialog;
+ dialog.set_data<int> ("finc_dialog", FinancialDialog.RATE_DIALOG);
+ dialog.response.connect (finc_response_cb);
+ dialog = financial_ui.get_object ("sln_dialog") as Gtk.Dialog;
+ dialog.set_data<int> ("finc_dialog", FinancialDialog.SLN_DIALOG);
+ dialog.response.connect (finc_response_cb);
+ dialog = financial_ui.get_object ("syd_dialog") as Gtk.Dialog;
+ dialog.set_data<int> ("finc_dialog", FinancialDialog.SYD_DIALOG);
+ dialog.response.connect (finc_response_cb);
+ dialog = financial_ui.get_object ("term_dialog") as Gtk.Dialog;
+ dialog.set_data<int> ("finc_dialog", FinancialDialog.TERM_DIALOG);
+ dialog.response.connect (finc_response_cb);
+
+ // FIXME
+ /*for (var i = 0; finc_dialog_fields[i, 0] != null; i++)
+ {
+ for (var j = 0; finc_dialog_fields[i, j] != null; j++)
+ {
+ var o = financial_ui.get_object (finc_dialog_fields[i, j]);
+ o.set_data<int> ("finc_field", i);
+ o.set_data<int> ("finc_dialog", j);
+ o.activate.connect (finc_activate_cb);
+ }
+ }*/
+ }
+
+ private void update_bit_panel ()
+ {
+ if (bit_panel == null)
+ return;
+
+ var x = equation.get_number ();
+
+ uint64 bits = 0;
+ var enabled = x != null;
+ if (enabled)
+ {
+ var max = new Number.unsigned_integer (uint64.MAX);
+ var fraction = x.fractional_part ();
+ if (x.is_negative () || x.compare (max) > 0 || !fraction.is_zero ())
+ enabled = false;
+ else
+ bits = x.to_unsigned_integer ();
+ }
+
+ bit_panel.set_sensitive (enabled);
+ base_label.set_sensitive (enabled);
+
+ if (!enabled)
+ return;
+
+ var i = 0;
+ foreach (var label in bit_labels)
+ {
+ var text = " 0";
+ if ((bits & (1LL << i)) != 0)
+ text = " 1";
+ label.set_text (text);
+ i++;
+ }
+
+ var number_base = equation.number_base;
+ var label = "";
+ if (number_base != 8)
+ label += "%llioâ".printf (bits);
+ if (number_base != 10)
+ {
+ if (label != "")
+ label += " = ";
+ label += "%lliuââ".printf (bits);
+ }
+ if (number_base != 16)
+ {
+ if (label != "")
+ label += " = ";
+ label += "%lliXââ".printf (bits);
+ }
+
+ base_label.set_text (label);
+ }
+
+ private void base_combobox_changed_cb (Gtk.ComboBox combo)
+ {
+ var model = combo.get_model ();
+ Gtk.TreeIter iter;
+ combo.get_active_iter (out iter);
+ int value;
+ model.get (iter, 1, out value, -1);
+
+ set_programming_base (value);
+ }
+
+ private void base_changed_cb ()
+ {
+ if (mode != ButtonMode.PROGRAMMING)
+ return;
+
+ programming_base = equation.number_base;
+
+ var model = base_combo.get_model ();
+ Gtk.TreeIter iter;
+ var valid = model.get_iter_first (out iter);
+ while (valid)
+ {
+ int v;
+ model.get (iter, 1, out v, -1);
+ if (v == programming_base)
+ break;
+ valid = model.iter_next (ref iter);
+ }
+ if (!valid)
+ valid = model.get_iter_first (out iter);
+
+ base_combo.set_active_iter (iter);
+ }
+
+ private Gtk.Widget load_mode (ButtonMode mode)
+ {
+ const string objects[] = { "button_panel", "character_code_dialog", "currency_dialog",
+ "ctrm_dialog", "ddb_dialog", "fv_dialog", "gpm_dialog",
+ "pmt_dialog", "pv_dialog", "rate_dialog", "sln_dialog",
+ "syd_dialog", "term_dialog", "adjustment1", "adjustment2", null };
+
+ Gtk.Builder builder;
+ string builder_filename;
+ switch (mode)
+ {
+ default:
+ case ButtonMode.BASIC:
+ if (bas_panel != null)
+ return bas_panel;
+ builder = basic_ui = new Gtk.Builder ();
+ builder_filename = Path.build_filename (UI_DIR, "buttons-basic.ui");
+ break;
+ case ButtonMode.ADVANCED:
+ if (adv_panel != null)
+ return adv_panel;
+ builder = advanced_ui = new Gtk.Builder ();
+ builder_filename = Path.build_filename (UI_DIR, "buttons-advanced.ui");
+ break;
+ case ButtonMode.FINANCIAL:
+ if (fin_panel != null)
+ return fin_panel;
+ builder = financial_ui = new Gtk.Builder ();
+ builder_filename = Path.build_filename (UI_DIR, "buttons-financial.ui");
+ break;
+ case ButtonMode.PROGRAMMING:
+ if (prog_panel != null)
+ return prog_panel;
+ builder = programming_ui = new Gtk.Builder ();
+ builder_filename = Path.build_filename (UI_DIR, "buttons-programming.ui");
+ break;
+ }
+
+ // FIXME: Show dialog if failed to load
+ try
+ {
+ builder.add_objects_from_file (builder_filename, objects);
+ }
+ catch (Error e)
+ {
+ warning ("Error loading button UI: %s", e.message);
+ }
+ var panel = builder.get_object ("button_panel") as Gtk.Widget;
+ pack_end (panel, true, true, 0);
+
+ switch (mode)
+ {
+ default:
+ case ButtonMode.BASIC:
+ bas_panel = panel;
+ break;
+ case ButtonMode.ADVANCED:
+ adv_panel = panel;
+ break;
+ case ButtonMode.FINANCIAL:
+ fin_panel = panel;
+ break;
+ case ButtonMode.PROGRAMMING:
+ prog_panel = panel;
+ break;
+ }
+
+ /* Configure buttons */
+ /* Tooltip for the Pi button */
+ setup_button (builder, "pi", "Ï", _("Pi [Ctrl+P]"));
+ /* Tooltip for the Euler's Number button */
+ setup_button (builder, "eulers_number", "e", _("Eulerâs Number"));
+ setup_button (builder, "imaginary", "i", null);
+ setup_button (builder, "numeric_point", null, null);
+ /* Tooltip for the subscript button */
+ setup_button (builder, "subscript", null, _("Subscript mode [Alt]"));
+ /* Tooltip for the superscript button */
+ setup_button (builder, "superscript", null, _("Superscript mode [Ctrl]"));
+ /* Tooltip for the scientific exponent button */
+ setup_button (builder, "exponential", null, _("Scientific exponent [Ctrl+E]"));
+ /* Tooltip for the add button */
+ setup_button (builder, "add", "+", _("Add [+]"));
+ /* Tooltip for the subtract button */
+ setup_button (builder, "subtract", "â", _("Subtract [-]"));
+ /* Tooltip for the multiply button */
+ setup_button (builder, "multiply", "Ã", _("Multiply [*]"));
+ /* Tooltip for the divide button */
+ setup_button (builder, "divide", "Ã", _("Divide [/]"));
+ /* Tooltip for the modulus divide button */
+ setup_button (builder, "modulus_divide", " mod ", _("Modulus divide"));
+ /* Tooltip for the additional functions button */
+ setup_button (builder, "function", null, _("Additional Functions"));
+ /* Tooltip for the exponent button */
+ setup_button (builder, "x_pow_y", "^", _("Exponent [^ or **]"));
+ /* Tooltip for the square button */
+ setup_button (builder, "x_squared", "Â", _("Square [Ctrl+2]"));
+ /* Tooltip for the percentage button */
+ setup_button (builder, "percentage", "%", _("Percentage [%]"));
+ /* Tooltip for the factorial button */
+ setup_button (builder, "factorial", "!", _("Factorial [!]"));
+ /* Tooltip for the absolute value button */
+ setup_button (builder, "abs", "|", _("Absolute value [|]"));
+ /* Tooltip for the complex argument component button */
+ setup_button (builder, "arg", "Arg ", _("Complex argument"));
+ /* Tooltip for the complex conjugate button */
+ setup_button (builder, "conjugate", "conj ", _("Complex conjugate"));
+ /* Tooltip for the root button */
+ setup_button (builder, "root", "â", _("Root [Ctrl+R]"));
+ /* Tooltip for the square root button */
+ setup_button (builder, "square_root", "â", _("Square root [Ctrl+R]"));
+ /* Tooltip for the logarithm button */
+ setup_button (builder, "logarithm", "log ", _("Logarithm"));
+ /* Tooltip for the natural logarithm button */
+ setup_button (builder, "natural_logarithm", "ln ", _("Natural Logarithm"));
+ /* Tooltip for the sine button */
+ setup_button (builder, "sine", "sin ", _("Sine"));
+ /* Tooltip for the cosine button */
+ setup_button (builder, "cosine", "cos ", _("Cosine"));
+ /* Tooltip for the tangent button */
+ setup_button (builder, "tangent", "tan ", _("Tangent"));
+ /* Tooltip for the hyperbolic sine button */
+ setup_button (builder, "hyperbolic_sine", "sinh ", _("Hyperbolic Sine"));
+ /* Tooltip for the hyperbolic cosine button */
+ setup_button (builder, "hyperbolic_cosine", "cosh ", _("Hyperbolic Cosine"));
+ /* Tooltip for the hyperbolic tangent button */
+ setup_button (builder, "hyperbolic_tangent", "tanh ", _("Hyperbolic Tangent"));
+ /* Tooltip for the inverse button */
+ setup_button (builder, "inverse", "âÂ", _("Inverse [Ctrl+I]"));
+ /* Tooltip for the boolean AND button */
+ setup_button (builder, "and", "â", _("Boolean AND"));
+ /* Tooltip for the boolean OR button */
+ setup_button (builder, "or", "â", _("Boolean OR"));
+ /* Tooltip for the exclusive OR button */
+ setup_button (builder, "xor", "â", _("Boolean Exclusive OR"));
+ /* Tooltip for the boolean NOT button */
+ setup_button (builder, "not", "Â", _("Boolean NOT"));
+ /* Tooltip for the integer component button */
+ setup_button (builder, "integer_portion", "int ", _("Integer Component"));
+ /* Tooltip for the fractional component button */
+ setup_button (builder, "fractional_portion", "frac ", _("Fractional Component"));
+ /* Tooltip for the real component button */
+ setup_button (builder, "real_portion", "Re ", _("Real Component"));
+ /* Tooltip for the imaginary component button */
+ setup_button (builder, "imaginary_portion", "Im ", _("Imaginary Component"));
+ /* Tooltip for the ones' complement button */
+ setup_button (builder, "ones_complement", "ones ", _("Ones' Complement"));
+ /* Tooltip for the two's complement button */
+ setup_button (builder, "twos_complement", "twos ", _("Two's Complement"));
+ /* Tooltip for the truncate button */
+ setup_button (builder, "trunc", "trunc ", _("Truncate"));
+ /* Tooltip for the start group button */
+ setup_button (builder, "start_group", "(", _("Start Group [(]"));
+ /* Tooltip for the end group button */
+ setup_button (builder, "end_group", ")", _("End Group [)]"));
+ /* Tooltip for the memory button */
+ setup_button (builder, "memory", null, _("Memory"));
+ /* Tooltip for the insert character code button */
+ setup_button (builder, "character", null, _("Insert Character Code"));
+ /* Tooltip for the solve button */
+ setup_button (builder, "result", null, _("Calculate Result"));
+ /* Tooltip for the factor button */
+ setup_button (builder, "factor", null, _("Factorize [Ctrl+F]"));
+ /* Tooltip for the clear button */
+ setup_button (builder, "clear", null, _("Clear Display [Escape]"));
+ /* Tooltip for the undo button */
+ setup_button (builder, "undo", null, _("Undo [Ctrl+Z]"));
+ /* Tooltip for the shift left button */
+ setup_button (builder, "shift_left", null, _("Shift Left"));
+ /* Tooltip for the shift right button */
+ setup_button (builder, "shift_right", null, _("Shift Right"));
+ /* Tooltip for the compounding term button */
+ setup_button (builder, "finc_compounding_term", null, _("Compounding Term"));
+ /* Tooltip for the double declining depreciation button */
+ setup_button (builder, "finc_double_declining_depreciation", null, _("Double Declining Depreciation"));
+ /* Tooltip for the future value button */
+ setup_button (builder, "finc_future_value", null, _("Future Value"));
+ /* Tooltip for the financial term button */
+ setup_button (builder, "finc_term", null, _("Financial Term"));
+ /* Tooltip for the sum of the years digits depreciation button */
+ setup_button (builder, "finc_sum_of_the_years_digits_depreciation", null, _("Sum of the Years Digits Depreciation"));
+ /* Tooltip for the straight line depreciation button */
+ setup_button (builder, "finc_straight_line_depreciation", null, _("Straight Line Depreciation"));
+ /* Tooltip for the periodic interest rate button */
+ setup_button (builder, "finc_periodic_interest_rate", null, _("Periodic Interest Rate"));
+ /* Tooltip for the present value button */
+ setup_button (builder, "finc_present_value", null, _("Present Value"));
+ /* Tooltip for the periodic payment button */
+ setup_button (builder, "finc_periodic_payment", null, _("Periodic Payment"));
+ /* Tooltip for the gross profit margin button */
+ setup_button (builder, "finc_gross_profit_margin", null, _("Gross Profit Margin"));
+
+ /* Set special button data */
+ for (var i = 0; i < 16; i++)
+ {
+ var name = "calc_%d_button".printf (i);
+ var button = builder.get_object (name) as Gtk.Button;
+ if (button != null)
+ {
+ button.set_data<int> ("calc_digit", i);
+ button.set_label (equation.get_digit_text (i).to_string ());
+ button.clicked.connect ((widget) => { equation.insert_digit (widget.get_data<int> ("calc_digit")); });
+ }
+ }
+ var button = builder.get_object ("calc_subtract_button") as Gtk.Button;
+ if (button != null)
+ button.clicked.connect (() => { equation.insert_subtract (); });
+ button = builder.get_object ("calc_undo_button") as Gtk.Button;
+ if (button != null)
+ button.clicked.connect (() => { equation.undo (); });
+ button = builder.get_object ("calc_result_button") as Gtk.Button;
+ if (button != null)
+ button.clicked.connect (() => { equation.solve (); });
+ button = builder.get_object ("calc_clear_button") as Gtk.Button;
+ if (button != null)
+ button.clicked.connect (() => { equation.clear (); });
+ button = builder.get_object ("calc_memory_button") as Gtk.Button;
+ if (button != null)
+ button.clicked.connect (memory_cb);
+ button = builder.get_object ("calc_function_button") as Gtk.Button;
+ if (button != null)
+ button.clicked.connect (function_cb);
+ button = builder.get_object ("calc_factor_button") as Gtk.Button;
+ if (button != null)
+ button.clicked.connect (() => { equation.factorize (); });
+ button = builder.get_object ("calc_exponential_button") as Gtk.Button;
+ if (button != null)
+ button.clicked.connect (() => { equation.insert_exponent (); });
+ button = builder.get_object ("calc_shift_left_button") as Gtk.Button;
+ if (button != null)
+ button.clicked.connect (shift_left_cb);
+ button = builder.get_object ("calc_shift_right_button") as Gtk.Button;
+ if (button != null)
+ button.clicked.connect (shift_right_cb);
+ button = builder.get_object ("calc_character_button") as Gtk.Button;
+ if (button != null)
+ button.clicked.connect (insert_character_code_cb);
+ button = builder.get_object ("calc_numeric_point_button") as Gtk.Button;
+ if (button != null)
+ {
+ var serializer = equation.get_serializer ();
+ button.set_label (serializer.get_radix ().to_string ());
+ button.clicked.connect (() => { equation.insert_numeric_point (); });
+ }
+
+ var toggle_button = builder.get_object ("calc_superscript_button") as Gtk.ToggleButton;
+ if (toggle_button != null)
+ {
+ superscript_toggles.append (toggle_button);
+ if (equation.get_number_mode () == NumberMode.SUPERSCRIPT)
+ toggle_button.set_active (true);
+ toggle_button.clicked.connect (set_superscript_cb);
+ }
+ toggle_button = builder.get_object ("calc_subscript_button") as Gtk.ToggleButton;
+ if (toggle_button != null)
+ {
+ subscript_toggles.append (toggle_button);
+ if (equation.get_number_mode () == NumberMode.SUBSCRIPT)
+ toggle_button.set_active (true);
+ toggle_button.clicked.connect (set_subscript_cb);
+ }
+
+ if (mode == ButtonMode.PROGRAMMING)
+ {
+ base_label = builder.get_object ("base_label") as Gtk.Label;
+ character_code_dialog = builder.get_object ("character_code_dialog") as Gtk.Dialog;
+ character_code_dialog.response.connect (character_code_dialog_response_cb);
+ character_code_dialog.delete_event.connect (character_code_dialog_delete_cb);
+ character_code_entry = builder.get_object ("character_code_entry") as Gtk.Entry;
+ character_code_entry.activate.connect (character_code_dialog_activate_cb);
+
+ bit_panel = builder.get_object ("bit_table") as Gtk.Widget;
+ bit_labels = new List<Gtk.Label> ();
+ var i = 0;
+ while (true)
+ {
+ var name = "bit_label_%d".printf (i);
+ var label = builder.get_object (name) as Gtk.Label;
+ if (label == null)
+ break;
+ bit_labels.append (label);
+ name = "bit_eventbox_%d".printf (i);
+ var box = builder.get_object (name) as Gtk.EventBox;
+ box.set_data<int> ("bit_index", i);
+ box.button_press_event.connect (bit_toggle_cb);
+ i++;
+ }
+ bit_labels.reverse ();
+
+ base_combo = builder.get_object ("base_combo") as Gtk.ComboBox;
+ var model = new Gtk.ListStore (2, typeof (string), typeof (int));
+ base_combo.model = model;
+ Gtk.TreeIter iter;
+ model.append (out iter);
+ model.set (iter, 0,
+ /* Number display mode combo: Binary, e.g. 10011010010â */
+ _("Binary"), 1, 2, -1);
+ model.append (out iter);
+ model.set (iter, 0,
+ /* Number display mode combo: Octal, e.g. 2322â */
+ _("Octal"), 1, 8, -1);
+ model.append (out iter);
+ model.set (iter, 0,
+ /* Number display mode combo: Decimal, e.g. 1234 */
+ _("Decimal"), 1, 10, -1);
+ model.append (out iter);
+ model.set (iter, 0,
+ /* Number display mode combo: Hexadecimal, e.g. 4D2ââ */
+ _("Hexadecimal"), 1, 16, -1);
+ var renderer = new Gtk.CellRendererText ();
+ base_combo.pack_start (renderer, true);
+ base_combo.add_attribute (renderer, "text", 0);
+
+ base_combo.changed.connect (base_combobox_changed_cb);
+ equation.notify["number-base"].connect ((pspec) => { base_changed_cb (); } );
+ base_changed_cb ();
+ }
+
+ /* Setup financial functions */
+ if (mode == ButtonMode.FINANCIAL)
+ {
+ load_finc_dialogs ();
+
+ button = builder.get_object ("calc_finc_compounding_term_button") as Gtk.Button;
+ if (button != null)
+ {
+ button.set_data<string> ("finc_dialog", "ctrm_dialog");
+ button.clicked.connect (finc_cb);
+ }
+ button = builder.get_object ("calc_finc_double_declining_depreciation_button") as Gtk.Button;
+ if (button != null)
+ {
+ button.set_data<string> ("finc_dialog", "ddb_dialog");
+ button.clicked.connect (finc_cb);
+ }
+ button = builder.get_object ("calc_finc_future_value_button") as Gtk.Button;
+ if (button != null)
+ {
+ button.set_data<string> ("finc_dialog", "fv_dialog");
+ button.clicked.connect (finc_cb);
+ }
+ button = builder.get_object ("calc_finc_gross_profit_margin_button") as Gtk.Button;
+ if (button != null)
+ {
+ button.set_data<string> ("finc_dialog", "gpm_dialog");
+ button.clicked.connect (finc_cb);
+ }
+ button = builder.get_object ("calc_finc_periodic_payment_button") as Gtk.Button;
+ if (button != null)
+ {
+ button.set_data<string> ("finc_dialog", "pmt_dialog");
+ button.clicked.connect (finc_cb);
+ }
+ button = builder.get_object ("calc_finc_present_value_button") as Gtk.Button;
+ if (button != null)
+ {
+ button.set_data<string> ("finc_dialog", "pv_dialog");
+ button.clicked.connect (finc_cb);
+ }
+ button = builder.get_object ("calc_finc_periodic_interest_rate_button") as Gtk.Button;
+ if (button != null)
+ {
+ button.set_data<string> ("finc_dialog", "rate_dialog");
+ button.clicked.connect (finc_cb);
+ }
+ button = builder.get_object ("calc_finc_straight_line_depreciation_button") as Gtk.Button;
+ if (button != null)
+ {
+ button.set_data<string> ("finc_dialog", "sln_dialog");
+ button.clicked.connect (finc_cb);
+ }
+ button = builder.get_object ("calc_finc_sum_of_the_years_digits_depreciation_button") as Gtk.Button;
+ if (button != null)
+ {
+ button.set_data<string> ("finc_dialog", "syd_dialog");
+ button.clicked.connect (finc_cb);
+ }
+ button = builder.get_object ("calc_finc_term_button") as Gtk.Button;
+ if (button != null)
+ {
+ button.set_data<string> ("finc_dialog", "term_dialog");
+ button.clicked.connect (finc_cb);
+ }
+ }
+
+ builder.connect_signals (this);
+
+ update_bit_panel ();
+
+ return panel;
+ }
+
+ private void setup_button (Gtk.Builder builder, string name, string? data, string? tooltip)
+ {
+ var widget_name = "calc_%s_button".printf (name);
+ var button = builder.get_object (widget_name) as Gtk.Button;
+ if (button == null)
+ return;
+
+ if (data != null)
+ {
+ button.set_data<string> ("calc_text", data);
+ button.clicked.connect ((widget) => { equation.insert (widget.get_data<string> ("calc_text")); });
+ }
+
+ if (tooltip != null)
+ button.set_tooltip_text (tooltip);
+
+ button.get_accessible ().set_name (name);
+ }
+
+ private void converter_changed_cb ()
+ {
+ Unit from_unit, to_unit;
+ converter.get_conversion (out from_unit, out to_unit);
+ if (mode == ButtonMode.FINANCIAL)
+ {
+ equation.source_currency = from_unit.name;
+ equation.target_currency = to_unit.name;
+ }
+ else
+ {
+ equation.source_units = from_unit.name;
+ equation.target_units = to_unit.name;
+ }
+ }
+
+ private void load_buttons ()
+ {
+ if (!get_visible ())
+ return;
+
+ if (converter == null)
+ {
+ converter = new MathConverter (equation);
+ converter.changed.connect (converter_changed_cb);
+ pack_start (converter, false, true, 0);
+ }
+
+ var panel = load_mode (mode);
+ if (active_panel == panel)
+ return;
+
+ /* Hide old buttons */
+ if (active_panel != null)
+ active_panel.hide ();
+
+ /* Load and display new buttons */
+ active_panel = panel;
+ if (panel != null)
+ panel.show ();
+ }
+
+ public void set_programming_base (int number_base)
+ {
+ if (number_base == programming_base)
+ return;
+
+ programming_base = number_base;
+ notify_property ("programming-base");
+
+ if (mode == ButtonMode.PROGRAMMING)
+ equation.number_base = number_base;
+ }
+
+ public int get_programming_base ()
+ {
+ return programming_base;
+ }
+
+ private void shift_cb (Gtk.Widget widget)
+ {
+ equation.shift (widget.get_data<int> ("shiftcount"));
+ }
+
+ private void popup_button_menu (Gtk.Button button, Gtk.Menu menu)
+ {
+ menu.set_data<Gtk.Widget> ("button", button);
+ menu.popup (null, null, button_menu_position_func, 1, Gtk.get_current_event_time ());
+ }
+
+ private void button_menu_position_func (Gtk.Menu menu, out int x, out int y, out bool push_in)
+ {
+ var button = menu.get_data<Gtk.Button> ("button");
+ int origin_x, origin_y;
+ button.get_window ().get_origin (out origin_x, out origin_y);
+ var border = button.get_border_width ();
+ Gtk.Allocation allocation;
+ button.get_allocation (out allocation);
+ x = (int) (origin_x + allocation.x + border);
+ y = (int) (origin_y + allocation.y + border);
+ }
+
+ private void memory_cb (Gtk.Widget widget)
+ {
+ var popup = new MathVariablePopup (equation);
+ popup.set_transient_for (widget.get_toplevel () as Gtk.Window);
+
+ Gtk.Allocation allocation;
+ widget.get_allocation (out allocation);
+ int x, y;
+ widget.get_window ().get_root_coords (allocation.x, allocation.y, out x, out y);
+ popup.move (x, y);
+ popup.show ();
+ }
+
+ private void shift_left_cb (Gtk.Button button)
+ {
+ if (shift_left_menu == null)
+ {
+ shift_left_menu = new Gtk.Menu ();
+ shift_left_menu.set_reserve_toggle_size (false);
+
+ for (var i = 1; i < 16; i++)
+ {
+ string format;
+ if (i < 10)
+ {
+ /* Left Shift Popup: Menu item to shift left by n places (n < 10) */
+ format = ngettext ("_%d place", "_%d places", i);
+ }
+ else
+ {
+ /* Left Shift Popup: Menu item to shift left by n places (n >= 10) */
+ format = ngettext ("%d place", "%d places", i);
+ }
+ var text = format.printf (i);
+ var label = new Gtk.Label.with_mnemonic (text);
+
+ var item = new Gtk.MenuItem ();
+ item.set_data<int> ("shiftcount", i);
+ item.add (label);
+ shift_left_menu.append (item);
+ item.activate.connect (shift_cb);
+
+ label.show ();
+ item.show ();
+ }
+ }
+
+ popup_button_menu (button, shift_left_menu);
+ }
+
+ private void shift_right_cb (Gtk.Button button)
+ {
+ if (shift_right_menu == null)
+ {
+ shift_right_menu = new Gtk.Menu ();
+ shift_right_menu.set_reserve_toggle_size (false);
+
+ for (var i = 1; i < 16; i++)
+ {
+ string format;
+ if (i < 10)
+ {
+ /* Right Shift Popup: Menu item to shift right by n places (n < 10) */
+ format = ngettext ("_%d place", "_%d places", i);
+ }
+ else
+ {
+ /* Right Shift Popup: Menu item to shift right by n places (n >= 10) */
+ format = ngettext ("%d place", "%d places", i);
+ }
+ var text = format.printf (i);
+ var label = new Gtk.Label.with_mnemonic (text);
+
+ var item = new Gtk.MenuItem ();
+ item.set_data<int> ("shiftcount", -i);
+ item.add (label);
+ shift_right_menu.append (item);
+ item.activate.connect (shift_cb);
+
+ label.show ();
+ item.show ();
+ }
+ }
+
+ popup_button_menu (button, shift_right_menu);
+ }
+
+ private void function_cb (Gtk.Button button)
+ {
+ if (function_menu == null)
+ {
+ function_menu = new Gtk.Menu ();
+ function_menu.set_reserve_toggle_size (false);
+
+ /* Tooltip for the integer component button */
+ add_function_menu_item (function_menu, _("Integer Component"), "int ");
+ /* Tooltip for the fractional component button */
+ add_function_menu_item (function_menu, _("Fractional Component"), "frac ");
+ /* Tooltip for the round button */
+ add_function_menu_item (function_menu, _("Round"), "round ");
+ /* Tooltip for the floor button */
+ add_function_menu_item (function_menu, _("Floor"), "floor ");
+ /* Tooltip for the ceiling button */
+ add_function_menu_item (function_menu, _("Ceiling"), "ceil ");
+ /* Tooltip for the ceiling button */
+ add_function_menu_item (function_menu, _("Sign"), "sgn ");
+ }
+
+ popup_button_menu (button, function_menu);
+ }
+
+ private void add_function_menu_item (Gtk.Menu menu, string label, string function)
+ {
+ var item = new Gtk.MenuItem.with_label (label);
+ item.set_data<string> ("function", function);
+ menu.append (item);
+ item.activate.connect ((widget) => { equation.insert (widget.get_data<string> ("function")); });
+ item.show ();
+ }
+
+ private void finc_cb (Gtk.Widget widget)
+ {
+ var name = widget.get_data<string> ("finc_dialog");
+ var dialog = financial_ui.get_object (name) as Gtk.Dialog;
+ dialog.run ();
+ dialog.hide ();
+ }
+
+ private void insert_character_code_cb (Gtk.Widget widget)
+ {
+ character_code_dialog.present ();
+ }
+
+ private void finc_activate_cb (Gtk.Widget widget)
+ {
+ var dialog_type = widget.get_data<int> ("finc_dialog");
+ var field = widget.get_data<int> ("finc_field");
+
+ // FIXME
+ /*if (finc_dialog_fields[dialog_type, field+1] == null)
+ {
+ var dialog = widget.get_toplevel () as Gtk.Dialog;
+ if (dialog.is_toplevel ())
+ {
+ dialog.response (Gtk.ResponseType.OK);
+ return;
+ }
+ }
+ else
+ {
+ var next_widget = financial_ui.get_object (finc_dialog_fields[dialog_type, field+1]) as Gtk.Widget;
+ next_widget.grab_focus ();
+ }*/
+ }
+
+ private void finc_response_cb (Gtk.Widget widget, int response_id)
+ {
+ if (response_id != Gtk.ResponseType.OK)
+ return;
+
+ var dialog = widget.get_data<int> ("finc_dialog");
+
+ Number arg[4];
+ // FIXME
+ /*for (var i = 0; i < 4; i++)
+ {
+ if (finc_dialog_fields[dialog, i] == null)
+ continue;
+ var entry = financial_ui.get_object (finc_dialog_fields[dialog, i]) as Gtk.Entry;
+ arg[i] = mp_set_from_string (entry.get_text ());
+ entry.set_text ("0");
+ }
+ var first_entry = financial_ui.get_object (finc_dialog_fields[dialog, 0]) as Gtk.Entry;
+ first_entry.grab_focus ();*/
+
+ do_finc_expression (equation, (FinancialDialog) dialog, arg[0], arg[1], arg[2], arg[3]);
+ }
+
+ private void character_code_dialog_response_cb (Gtk.Widget dialog, int response_id)
+ {
+ var text = character_code_entry.get_text ();
+
+ if (response_id == Gtk.ResponseType.OK)
+ {
+ var x = new Number.integer (0);
+ for (var i = 0; text[i] != '\0'; i++)
+ {
+ x = x.add (new Number.integer (text[i]));
+ x = x.shift (8);
+ }
+
+ equation.insert_number (x);
+ }
+
+ dialog.hide ();
+ }
+
+ private void character_code_dialog_activate_cb (Gtk.Widget entry)
+ {
+ character_code_dialog_response_cb (character_code_dialog, Gtk.ResponseType.OK);
+ }
+
+ private bool character_code_dialog_delete_cb (Gtk.Widget dialog, Gdk.EventAny event)
+ {
+ character_code_dialog_response_cb (dialog, Gtk.ResponseType.CANCEL);
+ return true;
+ }
+
+ private bool bit_toggle_cb (Gtk.Widget event_box, Gdk.EventButton event)
+ {
+ equation.toggle_bit (event_box.get_data<int> ("bit_index"));
+ return true;
+ }
+
+ private void remove_trailing_spaces ()
+ {
+ var insert_mark = equation.get_insert ();
+ Gtk.TextIter start, end;
+ equation.get_iter_at_mark (out end, insert_mark);
+ start = end;
+ while (start.backward_char ())
+ {
+ if (!start.get_char ().isspace ())
+ break;
+ equation.delete (ref start, ref end);
+ }
+ }
+
+ private void set_superscript_cb (Gtk.Button widget)
+ {
+ var button = widget as Gtk.ToggleButton;
+
+ if (button.get_active ())
+ {
+ equation.set_number_mode (NumberMode.SUPERSCRIPT);
+ if (!equation.get_has_selection ())
+ remove_trailing_spaces ();
+ }
+ else if (equation.get_number_mode () == NumberMode.SUPERSCRIPT)
+ equation.set_number_mode (NumberMode.NORMAL);
+ }
+
+ private void set_subscript_cb (Gtk.Button widget)
+ {
+ var button = widget as Gtk.ToggleButton;
+
+ if (button.get_active ())
+ {
+ equation.set_number_mode (NumberMode.SUBSCRIPT);
+ if (!equation.get_has_selection ())
+ remove_trailing_spaces ();
+ }
+ else if (equation.get_number_mode () == NumberMode.SUBSCRIPT)
+ equation.set_number_mode (NumberMode.NORMAL);
+ }
+
+ private void number_mode_changed_cb ()
+ {
+ var mode = equation.get_number_mode ();
+ foreach (var toggle in superscript_toggles)
+ toggle.set_active (mode == NumberMode.SUPERSCRIPT);
+ foreach (var toggle in subscript_toggles)
+ toggle.set_active (mode == NumberMode.SUBSCRIPT);
+ }
+}
diff --git a/src/math-converter.vala b/src/math-converter.vala
new file mode 100644
index 0000000..864fa76
--- /dev/null
+++ b/src/math-converter.vala
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2008-2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public class MathConverter : Gtk.Box
+{
+ private MathEquation equation;
+
+ private string category;
+
+ private Gtk.ComboBox from_combo;
+ private Gtk.ComboBox to_combo;
+
+ private Gtk.Label result_label;
+
+ public signal void changed ();
+
+ public MathConverter (MathEquation equation)
+ {
+ Object (orientation: Gtk.Orientation.HORIZONTAL);
+ this.equation = equation;
+
+ spacing = 6;
+
+ var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 0);
+ hbox.show ();
+ pack_start (hbox, false, true, 0);
+
+ from_combo = new Gtk.ComboBox ();
+
+ var renderer = new Gtk.CellRendererText ();
+ from_combo.pack_start (renderer, true);
+ from_combo.add_attribute (renderer, "text", 0);
+ from_combo.set_cell_data_func (renderer, from_cell_data_func);
+ from_combo.changed.connect (from_combobox_changed_cb);
+ from_combo.show ();
+ hbox.pack_start (from_combo, false, true, 0);
+
+ var label = new Gtk.Label (/* Label that is displayed between the two conversion combo boxes, e.g. "[degrees] in [radians]" */
+ _(" in "));
+ label.show ();
+ hbox.pack_start (label, false, true, 5);
+
+ to_combo = new Gtk.ComboBox ();
+ renderer = new Gtk.CellRendererText ();
+ to_combo.pack_start (renderer, true);
+ to_combo.add_attribute (renderer, "text", 0);
+ to_combo.changed.connect (to_combobox_changed_cb);
+ to_combo.show ();
+ hbox.pack_start (to_combo, false, true, 0);
+
+ var swap_button = new Gtk.Button.with_label ("â");
+ swap_button.set_tooltip_text (/* Tooltip for swap conversion button */
+ _("Switch conversion units"));
+ swap_button.set_relief (Gtk.ReliefStyle.NONE);
+ swap_button.clicked.connect (swap_button_clicked_cb);
+ swap_button.show ();
+ hbox.pack_start (swap_button, false, true, 0);
+
+ result_label = new Gtk.Label ("");
+ result_label.set_alignment (1.0f, 0.5f);
+ result_label.sensitive = false;
+ result_label.show ();
+ pack_start (result_label, true, true, 0);
+
+ CurrencyManager.get_default ().updated.connect (() => { update_result_label (); });
+ equation.notify["display"].connect ((pspec) => { update_result_label (); });
+
+ update_from_model ();
+ }
+
+ public void set_category (string? category)
+ {
+ if (this.category == category)
+ return;
+ this.category = category;
+
+ update_from_model ();
+ }
+
+ public string get_category ()
+ {
+ return category;
+ }
+
+ public void set_conversion (/*string category,*/ string unit_a, string unit_b)
+ {
+ var ua = UnitManager.get_default ().get_unit_by_name (unit_a);
+ var ub = UnitManager.get_default ().get_unit_by_name (unit_b);
+ if (ua == null || ub == null)
+ {
+ /* Select the first unit */
+ var model = from_combo.get_model ();
+ Gtk.TreeIter iter;
+ if (model.get_iter_first (out iter))
+ {
+ Gtk.TreeIter child_iter;
+ while (model.iter_children (out child_iter, iter))
+ iter = child_iter;
+ from_combo.set_active_iter (iter);
+ }
+ return;
+ }
+
+ set_active_unit (from_combo, null, ua);
+ set_active_unit (to_combo, null, ub);
+ }
+
+ public void get_conversion (out Unit from_unit, out Unit to_unit)
+ {
+ Gtk.TreeIter from_iter, to_iter;
+
+ from_combo.get_active_iter (out from_iter);
+ to_combo.get_active_iter (out to_iter);
+
+ from_combo.get_model ().get (from_iter, 2, out from_unit, -1);
+ to_combo.get_model ().get (to_iter, 2, out to_unit, -1);
+ }
+
+ private void update_result_label ()
+ {
+ var x = equation.get_number ();
+ if (x == null)
+ return;
+
+ var z = convert_equation (x);
+ result_label.sensitive = z != null;
+ if (z != null)
+ {
+ Unit source_unit, target_unit;
+ get_conversion (out source_unit, out target_unit);
+
+ var source_text = source_unit.format (x);
+ var target_text = target_unit.format (z);
+ result_label.set_text ("%s = %s".printf (source_text, target_text));
+ }
+ }
+
+ private void update_from_model ()
+ {
+ var from_model = new Gtk.TreeStore (3, typeof (string), typeof (UnitCategory), typeof (Unit));
+
+ if (category == null)
+ {
+ var categories = UnitManager.get_default ().get_categories ();
+ foreach (var category in categories)
+ {
+ Gtk.TreeIter parent;
+ from_model.append (out parent, null);
+ from_model.set (parent, 0, category.display_name, 1, category, -1);
+
+ foreach (var unit in category.get_units ())
+ {
+ Gtk.TreeIter iter;
+ from_model.append (out iter, parent);
+ from_model.set (iter, 0, unit.display_name, 1, category, 2, unit, -1);
+ }
+ }
+ }
+ else
+ {
+ var c = UnitManager.get_default ().get_category (category);
+ foreach (var unit in c.get_units ())
+ {
+ Gtk.TreeIter iter;
+ from_model.append (out iter, null);
+ from_model.set (iter, 0, unit.display_name, 1, c, 2, unit, -1);
+ }
+ }
+
+ from_combo.model = from_model;
+ }
+
+ private bool iter_is_unit (Gtk.TreeModel model, Gtk.TreeIter iter, Unit unit)
+ {
+ Unit u;
+ model.get (iter, 2, out u, -1);
+ return u == unit;
+ }
+
+ private bool set_active_unit (Gtk.ComboBox combo, Gtk.TreeIter? iter, Unit unit)
+ {
+ var model = combo.get_model ();
+ if (iter != null && iter_is_unit (model, iter, unit))
+ {
+ combo.set_active_iter (iter);
+ return true;
+ }
+
+ Gtk.TreeIter child_iter;
+ if (!model.iter_children (out child_iter, iter))
+ return false;
+
+ do
+ {
+ if (set_active_unit (combo, child_iter, unit))
+ return true;
+ } while (model.iter_next (ref child_iter));
+
+ return false;
+ }
+
+ private void from_combobox_changed_cb ()
+ {
+ var model = from_combo.get_model ();
+ Gtk.TreeIter iter;
+ if (!from_combo.get_active_iter (out iter))
+ return;
+ UnitCategory category;
+ Unit unit;
+ model.get (iter, 1, out category, 2, out unit, -1);
+
+ /* Set the to combobox to be the list of units can be converted to */
+ var to_model = new Gtk.ListStore (3, typeof (string), typeof (UnitCategory), typeof (Unit));
+ foreach (var u in category.get_units ())
+ {
+ if (u == unit)
+ continue;
+ to_model.append (out iter);
+ to_model.set (iter, 0, u.display_name, 1, category, 2, u, -1);
+ }
+ to_combo.model = to_model;
+
+ /* Select the first possible unit */
+ to_combo.set_active (0);
+ }
+
+ private void to_combobox_changed_cb ()
+ {
+ /* Conversion must have changed */
+ update_result_label ();
+ changed ();
+ }
+
+ private void from_cell_data_func (Gtk.CellLayout cell_layout, Gtk.CellRenderer cell, Gtk.TreeModel tree_model, Gtk.TreeIter iter)
+ {
+ cell.set ("sensitive", !tree_model.iter_has_child (iter));
+ }
+
+ private void swap_button_clicked_cb ()
+ {
+ var x = equation.get_number ();
+ if (x != null)
+ {
+ var z = convert_equation (x);
+ if (z != null)
+ equation.set_number (z);
+ }
+
+ Unit from_unit, to_unit;
+ get_conversion (out from_unit, out to_unit);
+ set_active_unit (from_combo, null, to_unit);
+ set_active_unit (to_combo, null, from_unit);
+
+ update_result_label ();
+ }
+
+ private Number? convert_equation (Number x)
+ {
+ Gtk.TreeIter from_iter, to_iter;
+ if (!from_combo.get_active_iter (out from_iter))
+ return null;
+ if (!to_combo.get_active_iter (out to_iter))
+ return null;
+
+ UnitCategory category;
+ Unit source_unit, target_unit;
+ from_combo.model.get (from_iter, 1, out category, 2, out source_unit, -1);
+ to_combo.model.get (to_iter, 2, out target_unit, -1);
+
+ return category.convert (x, source_unit, target_unit);
+ }
+}
diff --git a/src/math-display.vala b/src/math-display.vala
new file mode 100644
index 0000000..5ca12fc
--- /dev/null
+++ b/src/math-display.vala
@@ -0,0 +1,337 @@
+/*
+ * Copyright (C) 2008-2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public class MathDisplay : Gtk.Viewport
+{
+ /* Equation being displayed */
+ private MathEquation _equation;
+ public MathEquation equation { get { return _equation; } }
+
+ /* Display widget */
+ Gtk.TextView text_view;
+
+ /* Buffer that shows errors etc */
+ Gtk.TextBuffer info_buffer;
+
+ /* Spinner widget that shows if we're calculating a response */
+ Gtk.Spinner spinner;
+
+ public MathDisplay (MathEquation equation)
+ {
+ _equation = equation;
+
+ var main_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ add (main_box);
+
+ text_view = new Gtk.TextView.with_buffer (equation);
+ text_view.set_wrap_mode (Gtk.WrapMode.WORD);
+ text_view.set_accepts_tab (false);
+ text_view.set_pixels_above_lines (8);
+ text_view.set_pixels_below_lines (2);
+ /* TEMP: Disabled for now as GTK+ doesn't properly render a right aligned right margin, see bug #482688 */
+ /*text_view.set_right_margin (6);*/
+ text_view.set_justification (Gtk.Justification.RIGHT);
+ text_view.ensure_style ();
+ var font_desc = text_view.get_style ().font_desc.copy ();
+ font_desc.set_size (16 * Pango.SCALE);
+ text_view.modify_font (font_desc);
+ text_view.set_name ("displayitem");
+ text_view.get_accessible ().set_role (Atk.Role.EDITBAR);
+ //FIXME:<property name="AtkObject::accessible-description" translatable="yes" comments="Accessible description for the area in which results are displayed">Result Region</property>
+ text_view.key_press_event.connect (key_press_cb);
+
+ main_box.pack_start (text_view, true, true, 0);
+
+ var info_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
+ main_box.pack_start (info_box, false, true, 0);
+
+ var info_view = new Gtk.TextView ();
+ info_view.set_wrap_mode (Gtk.WrapMode.WORD);
+ info_view.set_can_focus (true); // FIXME: This should be false but it locks the cursor inside the main view for some reason
+ info_view.set_cursor_visible (false); // FIXME: Just here so when incorrectly gets focus doesn't look editable
+ info_view.set_editable (false);
+ info_view.set_justification (Gtk.Justification.RIGHT);
+ /* TEMP: Disabled for now as GTK+ doesn't properly render a right aligned right margin, see bug #482688 */
+ /*info_view.set_right_margin (6);*/
+ info_box.pack_start (info_view, true, true, 0);
+ info_buffer = info_view.get_buffer ();
+
+ spinner = new Gtk.Spinner ();
+ info_box.pack_end (spinner, false, false, 0);
+ style = info_view.get_style ();
+ for (var i = 0; i < 5; i++)
+ modify_bg (i, style.base[i]);
+
+ info_box.show ();
+ info_view.show ();
+ text_view.show ();
+ main_box.show ();
+
+ equation.notify["status"].connect ((pspec) => { status_changed_cb (); });
+ status_changed_cb ();
+ }
+
+ protected override bool key_press_event (Gdk.EventKey event)
+ {
+ return text_view.key_press_event (event);
+ }
+
+ private bool key_press_cb (Gdk.EventKey event)
+ {
+ /* Treat keypad keys as numbers even when numlock is off */
+ uint new_keyval = 0;
+ switch (event.keyval)
+ {
+ case Gdk.Key.KP_Insert:
+ new_keyval = Gdk Key 0;
+ break;
+ case Gdk.Key.KP_End:
+ new_keyval = Gdk Key 1;
+ break;
+ case Gdk.Key.KP_Down:
+ new_keyval = Gdk Key 2;
+ break;
+ case Gdk.Key.KP_Page_Down:
+ new_keyval = Gdk Key 3;
+ break;
+ case Gdk.Key.KP_Left:
+ new_keyval = Gdk Key 4;
+ break;
+ case Gdk.Key.KP_Begin: /* This is apparently what "5" does when numlock is off. */
+ new_keyval = Gdk Key 5;
+ break;
+ case Gdk.Key.KP_Right:
+ new_keyval = Gdk Key 6;
+ break;
+ case Gdk.Key.KP_Home:
+ new_keyval = Gdk Key 7;
+ break;
+ case Gdk.Key.KP_Up:
+ new_keyval = Gdk Key 8;
+ break;
+ case Gdk.Key.KP_Page_Up:
+ new_keyval = Gdk Key 9;
+ break;
+ }
+
+ if (new_keyval != 0)
+ {
+ var new_event = event; // FIXME: Does this copy?
+ new_event.keyval = new_keyval;
+ return key_press_event (new_event);
+ }
+
+ var state = event.state & (Gdk.ModifierType.CONTROL_MASK | Gdk.ModifierType.MOD1_MASK);
+ var c = Gdk.keyval_to_unicode (event.keyval);
+
+ /* Solve on enter */
+ if (event.keyval == Gdk.Key.Return || event.keyval == Gdk.Key.KP_Enter)
+ {
+ equation.solve ();
+ return true;
+ }
+
+ /* Clear on escape */
+ if ((event.keyval == Gdk.Key.Escape && state == 0) ||
+ (event.keyval == Gdk.Key.BackSpace && state == Gdk.ModifierType.CONTROL_MASK) ||
+ (event.keyval == Gdk.Key.Delete && state == Gdk.ModifierType.SHIFT_MASK))
+ {
+ equation.clear ();
+ return true;
+ }
+
+ /* Numeric keypad will often insert '.' regardless of locale */
+ if (event.keyval == Gdk.Key.KP_Decimal)
+ {
+ equation.insert_numeric_point ();
+ return true;
+ }
+
+ /* Substitute */
+ if (state == 0)
+ {
+ if (c == '*')
+ {
+ equation.insert ("Ã");
+ return true;
+ }
+ if (c == '/')
+ {
+ equation.insert ("Ã");
+ return true;
+ }
+ if (c == '-')
+ {
+ equation.insert_subtract ();
+ return true;
+ }
+ }
+
+ /* Shortcuts */
+ if (state == Gdk.ModifierType.CONTROL_MASK)
+ {
+ switch (event.keyval)
+ {
+ case Gdk.Key.bracketleft:
+ equation.insert ("â");
+ return true;
+ case Gdk.Key.bracketright:
+ equation.insert ("â");
+ return true;
+ case Gdk.Key.e:
+ equation.insert_exponent ();
+ return true;
+ case Gdk.Key.f:
+ equation.factorize ();
+ return true;
+ case Gdk.Key.i:
+ equation.insert ("âÂ");
+ return true;
+ case Gdk.Key.p:
+ equation.insert ("Ï");
+ return true;
+ case Gdk.Key.r:
+ equation.insert ("â");
+ return true;
+ case Gdk.Key.u:
+ equation.insert ("Â");
+ return true;
+ case Gdk.Key.minus:
+ equation.insert ("â");
+ return true;
+ case Gdk.Key.apostrophe:
+ equation.insert ("Â");
+ return true;
+ }
+ }
+ if (state == Gdk.ModifierType.MOD1_MASK)
+ {
+ switch (event.keyval)
+ {
+ case Gdk.Key.bracketleft:
+ equation.insert ("â");
+ return true;
+ case Gdk.Key.bracketright:
+ equation.insert ("â");
+ return true;
+ }
+ }
+
+ if (state == Gdk.ModifierType.CONTROL_MASK || equation.get_number_mode () == NumberMode.SUPERSCRIPT)
+ {
+ switch (event.keyval)
+ {
+ case Gdk Key 0:
+ case Gdk.Key.KP_0:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 1:
+ case Gdk.Key.KP_1:
+ equation.insert ("Â");
+ return true;
+ case Gdk Key 2:
+ case Gdk.Key.KP_2:
+ equation.insert ("Â");
+ return true;
+ case Gdk Key 3:
+ case Gdk.Key.KP_3:
+ equation.insert ("Â");
+ return true;
+ case Gdk Key 4:
+ case Gdk.Key.KP_4:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 5:
+ case Gdk.Key.KP_5:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 6:
+ case Gdk.Key.KP_6:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 7:
+ case Gdk.Key.KP_7:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 8:
+ case Gdk.Key.KP_8:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 9:
+ case Gdk.Key.KP_9:
+ equation.insert ("â");
+ return true;
+ }
+ }
+ else if (state == Gdk.ModifierType.MOD1_MASK || equation.get_number_mode () == NumberMode.SUBSCRIPT)
+ {
+ switch (event.keyval)
+ {
+ case Gdk Key 0:
+ case Gdk.Key.KP_0:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 1:
+ case Gdk.Key.KP_1:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 2:
+ case Gdk.Key.KP_2:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 3:
+ case Gdk.Key.KP_3:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 4:
+ case Gdk.Key.KP_4:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 5:
+ case Gdk.Key.KP_5:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 6:
+ case Gdk.Key.KP_6:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 7:
+ case Gdk.Key.KP_7:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 8:
+ case Gdk.Key.KP_8:
+ equation.insert ("â");
+ return true;
+ case Gdk Key 9:
+ case Gdk.Key.KP_9:
+ equation.insert ("â");
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ private void status_changed_cb ()
+ {
+ info_buffer.set_text (equation.status, -1);
+ if (equation.get_in_solve () && !spinner.get_visible ())
+ {
+ spinner.show ();
+ spinner.start ();
+ }
+ else if (!equation.get_in_solve () && spinner.get_visible ())
+ {
+ spinner.hide ();
+ spinner.stop ();
+ }
+ }
+}
diff --git a/src/math-equation.vala b/src/math-equation.vala
new file mode 100644
index 0000000..6caf1be
--- /dev/null
+++ b/src/math-equation.vala
@@ -0,0 +1,1191 @@
+/*
+ * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (C) 2008-2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public enum NumberMode
+{
+ NORMAL,
+ SUPERSCRIPT,
+ SUBSCRIPT
+}
+
+/* Expression mode state */
+private struct MathEquationState
+{
+ Number ans; /* Previously calculated answer */
+ string expression; /* Expression entered by user */
+ int ans_start; /* Start character for ans variable in expression */
+ int ans_end; /* End character for ans variable in expression */
+ int cursor; /* ??? */
+ NumberMode number_mode; /* ??? */
+ bool can_super_minus; /* true if entering minus can generate a superscript minus */
+ bool entered_multiply; /* Last insert was a multiply character */
+ string status; /* Equation status */
+}
+
+private struct SolveData
+{
+ Number? number_result;
+ string text_result;
+ string error;
+}
+
+public class MathEquation : Gtk.TextBuffer
+{
+ private Gtk.TextTag ans_tag;
+
+ /* Word size in bits */
+ private int _word_size;
+ public int word_size
+ {
+ get { return _word_size; }
+ set
+ {
+ if (_word_size == value)
+ return;
+ _word_size = value;
+ notify_property ("word-size");
+ }
+ }
+
+ private string _source_currency;
+ public string source_currency
+ {
+ owned get { return _source_currency; }
+ set
+ {
+ if (_source_currency == value)
+ return;
+ _source_currency = value;
+ }
+ }
+
+ private string _target_currency;
+ public string target_currency
+ {
+ owned get { return _target_currency; }
+ set
+ {
+ if (_target_currency == value)
+ return;
+ _target_currency = value;
+ }
+ }
+
+ private string _source_units;
+ public string source_units
+ {
+ owned get { return _source_units; }
+ set
+ {
+ if (_source_units == value)
+ return;
+ _source_units = value;
+ }
+ }
+
+ private string _target_units;
+ public string target_units
+ {
+ owned get { return _target_units; }
+ set
+ {
+ if (_target_units == value)
+ return;
+ _target_units = value;
+ }
+ }
+
+ public string display
+ {
+ owned get
+ {
+ Gtk.TextIter start, end;
+ get_bounds (out start, out end);
+ return get_text (start, end, false);
+ }
+ }
+
+ private AngleUnit angle_units; /* Units for trigonometric functions */
+ private NumberMode number_mode; /* ??? */
+ private bool can_super_minus; /* true if entering minus can generate a superscript minus */
+
+ private unichar digits[16]; /* Localized digits */
+
+ private Gtk.TextMark? ans_start_mark = null;
+ private Gtk.TextMark? ans_end_mark = null;
+
+ private MathEquationState state; /* Equation state */
+ private List<MathEquationState?> undo_stack; /* History of expression mode states */
+ private List<MathEquationState?> redo_stack;
+ private bool in_undo_operation;
+
+ private bool in_reformat;
+
+ private bool in_delete;
+
+ private bool in_solve;
+
+ private MathVariables variables;
+ private Serializer serializer;
+
+ private AsyncQueue<SolveData?> queue;
+
+ public MathEquation ()
+ {
+ undo_stack = new List<MathEquationState?> ();
+ redo_stack = new List<MathEquationState?> ();
+
+ insert_text.connect (pre_insert_text_cb);
+ delete_range.connect (pre_delete_range_cb);
+ insert_text.connect_after (insert_text_cb);
+ delete_range.connect_after (delete_range_cb);
+
+ /* Default to using untranslated digits, this is because it doesn't make sense in most languages and we need to make this optional.
+ * See https://bugzilla.gnome.org/show_bug.cgi?id=632661 */
+ var use_default_digits = true;
+
+ const unichar default_digits[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+ /* Digits localized for the given language */
+ var ds = _("0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F").split (",", -1);
+ for (var i = 0; i < 16; i++)
+ {
+ if (use_default_digits || ds[i] == null)
+ {
+ use_default_digits = true;
+ digits[i] = default_digits[i];
+ }
+ else
+ digits[i] = ds[i].get_char (0);
+ }
+
+ variables = new MathVariables ();
+
+ state.status = "";
+ word_size = 32;
+ angle_units = AngleUnit.DEGREES;
+ // FIXME: Pick based on locale
+ source_currency = "";
+ target_currency = "";
+ source_units = "";
+ target_units = "";
+ serializer = new Serializer (DisplayFormat.AUTOMATIC, 10, 9);
+ queue = new AsyncQueue<SolveData?> ();
+
+ state.ans = new Number.integer (0);
+
+ ans_tag = create_tag (null, "weight", Pango.Weight.BOLD, null);
+ }
+
+ public MathVariables get_variables ()
+ {
+ return variables;
+ }
+
+ private void get_ans_offsets (out int start, out int end)
+ {
+ Gtk.TextIter iter;
+
+ if (ans_start_mark == null)
+ {
+ start = end = -1;
+ return;
+ }
+
+ get_iter_at_mark (out iter, ans_start_mark);
+ start = iter.get_offset ();
+ get_iter_at_mark (out iter, ans_end_mark);
+ end = iter.get_offset ();
+ }
+
+ private void reformat_ans ()
+ {
+ if (ans_start_mark == null)
+ return;
+
+ string orig_ans_text;
+ string ans_text;
+ Gtk.TextIter ans_start, ans_end;
+
+ get_iter_at_mark (out ans_start, ans_start_mark);
+ get_iter_at_mark (out ans_end, ans_end_mark);
+ orig_ans_text = get_text (ans_start, ans_end, false);
+ ans_text = serializer.to_string (state.ans);
+ if (orig_ans_text != ans_text)
+ {
+ int start;
+
+ in_undo_operation = true;
+ in_reformat = true;
+
+ start = ans_start.get_offset ();
+ @delete (ref ans_start, ref ans_end);
+ insert_with_tags (ans_end, ans_text, -1, ans_tag, null);
+
+ /* There seems to be a bug in the marks as they alternate being the correct and incorrect ways. Reset them */
+ get_iter_at_offset (out ans_start, start);
+ move_mark (ans_start_mark, ans_start);
+ move_mark (ans_end_mark, ans_end);
+
+ in_reformat = false;
+ in_undo_operation = false;
+ }
+ get_iter_at_mark (out ans_start, ans_start_mark);
+ get_iter_at_mark (out ans_end, ans_end_mark);
+ }
+
+ private void reformat_separators ()
+ {
+ var in_number = false;
+ var in_radix = false;
+ var last_is_tsep = false;
+ var digit_offset = 0;
+
+ in_undo_operation = true;
+ in_reformat = true;
+
+ var text = display;
+ int ans_start, ans_end;
+ get_ans_offsets (out ans_start, out ans_end);
+ var offset = 0;
+ var index = 0;
+ unichar c;
+ while (text.get_next_char (ref index, out c))
+ {
+ var expect_tsep = number_base == 10 &&
+ serializer.get_show_thousands_separators () &&
+ in_number && !in_radix && !last_is_tsep &&
+ digit_offset > 0 && digit_offset % serializer.get_thousands_separator_count () == 0;
+ last_is_tsep = false;
+
+ /* Don't mess with ans */
+ if (index >= ans_start && offset <= ans_end)
+ {
+ in_number = in_radix = false;
+ continue;
+ }
+ if (c.isdigit ())
+ {
+ if (!in_number)
+ digit_offset = count_digits (text, index);
+ in_number = true;
+
+ /* Expected a thousands separator between these digits - insert it */
+ if (expect_tsep)
+ {
+ Gtk.TextIter iter;
+ get_iter_at_offset (out iter, offset);
+ (this as Gtk.TextBuffer).insert (ref iter, serializer.get_thousands_separator ().to_string (), -1);
+ offset++;
+ last_is_tsep = true;
+ }
+
+ digit_offset--;
+ }
+ else if (c == serializer.get_radix ())
+ in_number = in_radix = true;
+ else if (c == serializer.get_thousands_separator ())
+ {
+ /* Didn't expect thousands separator - delete it */
+ if (!expect_tsep && in_number)
+ {
+ Gtk.TextIter start, end;
+ get_iter_at_offset (out start, offset);
+ get_iter_at_offset (out end, offset + 1);
+ @delete (ref start, ref end);
+ offset--;
+ }
+ else
+ last_is_tsep = true;
+ }
+ else
+ {
+ in_number = in_radix = false;
+ }
+ }
+
+ in_reformat = false;
+ in_undo_operation = false;
+ }
+
+ private int count_digits (string text, int index)
+ {
+ var count = 0;
+ var following_separator = false;
+ unichar c;
+ while (text.get_next_char (ref index, out c))
+ {
+ /* Allow a thousands separator between digits follow a digit */
+ if (c == serializer.get_thousands_separator ())
+ {
+ if (following_separator)
+ return count;
+ following_separator = true;
+ }
+ else if (c.isdigit ())
+ {
+ following_separator = false;
+ count++;
+ }
+ else
+ return count;
+ }
+
+ return count;
+ }
+
+ private void reformat_display ()
+ {
+ /* Change ans */
+ reformat_ans ();
+
+ /* Add/remove thousands separators */
+ reformat_separators ();
+ }
+
+ private MathEquationState get_current_state ()
+ {
+ int ans_start = -1, ans_end = -1;
+
+ if (ans_start_mark != null)
+ {
+ Gtk.TextIter iter;
+ get_iter_at_mark (out iter, ans_start_mark);
+ ans_start = iter.get_offset ();
+ get_iter_at_mark (out iter, ans_end_mark);
+ ans_end = iter.get_offset ();
+ }
+
+ var s = MathEquationState ();
+ s.ans = state.ans;
+ s.expression = display;
+ s.ans_start = ans_start;
+ s.ans_end = ans_end;
+ get ("cursor-position", out s.cursor, null);
+ s.number_mode = number_mode;
+ s.can_super_minus = can_super_minus;
+ s.entered_multiply = state.entered_multiply;
+ s.status = state.status;
+
+ return s;
+ }
+
+ private void push_undo_stack ()
+ {
+ if (in_undo_operation)
+ return;
+
+ status = "";
+
+ /* Can't redo anymore */
+ redo_stack = new List<MathEquationState?> ();
+
+ state = get_current_state ();
+ undo_stack.prepend (state);
+ }
+
+ private void clear_ans (bool do_remove_tag)
+ {
+ if (ans_start_mark == null)
+ return;
+
+ if (do_remove_tag)
+ {
+ Gtk.TextIter start, end;
+
+ get_iter_at_mark (out start, ans_start_mark);
+ get_iter_at_mark (out end, ans_end_mark);
+ remove_tag (ans_tag, start, end);
+ }
+
+ delete_mark (ans_start_mark);
+ delete_mark (ans_end_mark);
+ ans_start_mark = null;
+ ans_end_mark = null;
+ }
+
+ private void apply_state (MathEquationState s)
+ {
+ Gtk.TextIter cursor;
+
+ /* Disable undo detection */
+ in_undo_operation = true;
+
+ state.ans = s.ans;
+ set_text (s.expression, -1);
+ get_iter_at_offset (out cursor, s.cursor);
+ place_cursor (cursor);
+ clear_ans (false);
+ if (s.ans_start >= 0)
+ {
+ Gtk.TextIter start;
+ get_iter_at_offset (out start, s.ans_start);
+ ans_start_mark = create_mark (null, start, false);
+ Gtk.TextIter end;
+ get_iter_at_offset (out end, s.ans_end);
+ ans_end_mark = create_mark (null, end, true);
+ apply_tag (ans_tag, start, end);
+ }
+
+ set_number_mode (s.number_mode);
+ can_super_minus = s.can_super_minus;
+ state.entered_multiply = s.entered_multiply;
+ status = s.status;
+
+ in_undo_operation = false;
+ }
+
+ public void copy ()
+ {
+ Gtk.TextIter start, end;
+ string text;
+
+ if (!get_selection_bounds (out start, out end))
+ get_bounds (out start, out end);
+
+ text = get_text (start, end, false);
+ Gtk.Clipboard.get (Gdk.Atom.NONE).set_text (text, -1);
+ }
+
+ public void paste ()
+ {
+ Gtk.Clipboard.get (Gdk.Atom.NONE).request_text (on_paste);
+ }
+
+ private void on_paste (Gtk.Clipboard clipboard, string? text)
+ {
+ if (text != null)
+ insert (text);
+ }
+
+ public void undo ()
+ {
+ if (undo_stack == null)
+ {
+ /* Error shown when trying to undo with no undo history */
+ status = _("No undo history");
+ return;
+ }
+
+ state = undo_stack.nth_data (0);
+ undo_stack.remove (state);
+ redo_stack.prepend (get_current_state ());
+
+ apply_state (state);
+ }
+
+ public void redo ()
+ {
+ if (redo_stack == null)
+ {
+ /* Error shown when trying to redo with no redo history */
+ status = _("No redo history");
+ return;
+ }
+
+ state = redo_stack.nth_data (0);
+ redo_stack.remove (state);
+ undo_stack.prepend (get_current_state ());
+
+ apply_state (state);
+ }
+
+ public unichar get_digit_text (uint digit)
+ {
+ if (digit >= 16)
+ return '?';
+ return digits[digit];
+ }
+
+ public void set_accuracy (int accuracy)
+ {
+ if (serializer.get_trailing_digits () == accuracy)
+ return;
+ serializer.set_trailing_digits (accuracy);
+ reformat_display ();
+ notify_property ("accuracy");
+ }
+
+ public int get_accuracy ()
+ {
+ return serializer.get_trailing_digits ();
+ }
+
+ public void set_show_thousands_separators (bool visible)
+ {
+ if (serializer.get_show_thousands_separators () == visible)
+ return;
+
+ serializer.set_show_thousands_separators (visible);
+ reformat_display ();
+ notify_property ("show-thousands-separators");
+ }
+
+ public bool get_show_thousands_separators ()
+ {
+ return serializer.get_show_thousands_separators ();
+ }
+
+ public void set_show_trailing_zeroes (bool visible)
+ {
+ if (serializer.get_show_trailing_zeroes () == visible)
+ return;
+
+ serializer.set_show_trailing_zeroes (visible);
+ reformat_display ();
+ notify_property ("show-trailing-zeroes");
+ }
+
+ public bool get_show_trailing_zeroes ()
+ {
+ return serializer.get_show_trailing_zeroes ();
+ }
+
+ public void set_number_format (DisplayFormat format)
+ {
+ if (serializer.get_number_format () == format)
+ return;
+
+ serializer.set_number_format (format);
+ reformat_display ();
+ notify_property ("number-format");
+ }
+
+ public DisplayFormat get_number_format ()
+ {
+ return serializer.get_number_format ();
+ }
+
+ public int number_base
+ {
+ get { return serializer.get_base (); }
+ set
+ {
+ if (serializer.get_base () == value)
+ return;
+
+ serializer.set_base (value);
+ reformat_display ();
+ notify_property ("number-base");
+ }
+ }
+
+ public void set_angle_units (AngleUnit angle_units)
+ {
+ if (this.angle_units == angle_units)
+ return;
+
+ this.angle_units = angle_units;
+ notify_property ("angle-units");
+ }
+
+ public AngleUnit get_angle_units ()
+ {
+ return angle_units;
+ }
+
+ public string status
+ {
+ owned get { return state.status; }
+ set
+ {
+ if (state.status == value)
+ return;
+
+ state.status = value;
+ notify_property ("status");
+ }
+ }
+
+ public bool is_empty ()
+ {
+ return get_char_count () == 0;
+ }
+
+ public bool is_result ()
+ {
+ return get_equation () == "ans";
+ }
+
+ public string get_equation ()
+ {
+ var text = display;
+ var eq_text = "";
+
+ var ans_start = -1, ans_end = -1;
+ if (ans_start_mark != null)
+ get_ans_offsets (out ans_start, out ans_end);
+
+ var last_is_digit = false;
+ var index = 0;
+ unichar c;
+ while (text.get_next_char (ref index, out c))
+ {
+ var is_digit = c.isdigit ();
+ var next_is_digit = false;
+ unichar next_char;
+ var i = index;
+ if (text.get_next_char (ref i, out next_char))
+ next_is_digit = next_char.isdigit ();
+
+ /* Replace ans text with variable */
+ if (index == ans_start)
+ {
+ eq_text += "ans";
+ index += ans_end - ans_start - 1;
+ is_digit = false;
+ continue;
+ }
+
+ /* Ignore thousands separators */
+ if (c == serializer.get_thousands_separator () && last_is_digit && next_is_digit)
+ ;
+ /* Substitute radix character */
+ else if (c == serializer.get_radix () && (last_is_digit || next_is_digit))
+ eq_text += ".";
+ else
+ eq_text += c.to_string ();
+
+ last_is_digit = is_digit;
+ }
+
+ text = eq_text;
+
+ return text;
+ }
+
+ public Number? get_number ()
+ {
+ if (is_result ())
+ return get_answer ();
+ else
+ return serializer.from_string (get_equation ());
+ }
+
+ public Serializer get_serializer ()
+ {
+ return serializer;
+ }
+
+ public void set_number_mode (NumberMode mode)
+ {
+ if (number_mode == mode)
+ return;
+
+ can_super_minus = mode == NumberMode.SUPERSCRIPT;
+
+ number_mode = mode;
+ notify_property ("number-mode");
+ }
+
+ public NumberMode get_number_mode ()
+ {
+ return number_mode;
+ }
+
+ public bool get_in_solve ()
+ {
+ return in_solve;
+ }
+
+ public Number get_answer ()
+ {
+ return state.ans;
+ }
+
+ public void store (string name)
+ {
+ var t = get_number ();
+ if (t == null)
+ status = _("No sane value to store");
+ else
+ variables.set (name, t);
+ }
+
+ public void recall (string name)
+ {
+ insert (name);
+ }
+
+ public new void set (string text)
+ {
+ set_text (text, -1);
+ clear_ans (false);
+ }
+
+ public void set_number (Number x)
+ {
+ /* Show the number in the user chosen format */
+ var text = serializer.to_string (x);
+ set_text (text, -1);
+ state.ans = x;
+
+ /* Mark this text as the answer variable */
+ Gtk.TextIter start, end;
+ get_bounds (out start, out end);
+ clear_ans (false);
+ ans_start_mark = create_mark (null, start, false);
+ ans_end_mark = create_mark (null, end, true);
+ apply_tag (ans_tag, start, end);
+ }
+
+ public new void insert (string text)
+ {
+ /* Replace ** with ^ (not on all keyboards) */
+ if (!get_has_selection () && text == "Ã" && state.entered_multiply)
+ {
+ Gtk.TextIter iter;
+ get_iter_at_mark (out iter, get_insert ());
+ (this as Gtk.TextBuffer).backspace (iter, true, true);
+ insert_at_cursor ("^", -1);
+ return;
+ }
+
+ /* Can't enter superscript minus after entering digits */
+ if ("âÂÂÂââââââ".index_of (text) >= 0 || text == "â")
+ can_super_minus = false;
+
+ /* Disable super/subscript mode when finished entering */
+ if ("ââÂÂÂââââââââââââââââ".index_of (text) >= 0)
+ set_number_mode (NumberMode.NORMAL);
+
+ delete_selection (false, false);
+ insert_at_cursor (text, -1);
+ }
+
+ public void insert_digit (uint digit)
+ {
+ const string subscript_digits[] = {"â", "â", "â", "â", "â", "â", "â", "â", "â", "â"};
+ const string superscript_digits[] = {"â", "Â", "Â", "Â", "â", "â", "â", "â", "â", "â"};
+
+ if (digit >= 16)
+ return;
+
+ if (number_mode == NumberMode.NORMAL || digit >= 10)
+ insert (get_digit_text (digit).to_string ());
+ else if (number_mode == NumberMode.SUPERSCRIPT)
+ insert (superscript_digits[digit]);
+ else if (number_mode == NumberMode.SUBSCRIPT)
+ insert (subscript_digits[digit]);
+ }
+
+ public void insert_numeric_point ()
+ {
+ insert (serializer.get_radix ().to_string ());
+ }
+
+ public void insert_number (Number x)
+ {
+ insert (serializer.to_string (x));
+ }
+
+ public void insert_exponent ()
+ {
+ insert ("Ã10");
+ set_number_mode (NumberMode.SUPERSCRIPT);
+ }
+
+ public void insert_subtract ()
+ {
+ if (number_mode == NumberMode.SUPERSCRIPT && can_super_minus)
+ {
+ insert ("â");
+ can_super_minus = false;
+ }
+ else
+ {
+ insert ("â");
+ set_number_mode (NumberMode.NORMAL);
+ }
+ }
+
+ private Number? parse (string text, out ErrorCode error_code = null, out string error_token = null)
+ {
+ var equation = new MEquation (this, text);
+ equation.base = serializer.get_base ();
+ equation.wordlen = word_size;
+ equation.angle_units = angle_units;
+
+ return equation.parse (out error_code, out error_token);
+ }
+
+ /*
+ * Executed in separate thread. It is thus not a good idea to write to anything
+ * in MathEquation but the async queue from here.
+ */
+ private void* solve_real ()
+ {
+ var solvedata = SolveData ();
+
+ var text = get_equation ();
+ /* Count the number of brackets and automatically add missing closing brackets */
+ var n_brackets = 0;
+ for (var i = 0; text[i] != '\0'; i++)
+ {
+ if (text[i] == '(')
+ n_brackets++;
+ else if (text[i] == ')')
+ n_brackets--;
+ }
+ while (n_brackets > 0)
+ {
+ text += ")";
+ n_brackets--;
+ }
+
+ ErrorCode error_code;
+ string error_token;
+ var z = parse (text, out error_code, out error_token);
+ switch (error_code)
+ {
+ case ErrorCode.NONE:
+ solvedata.number_result = z;
+ break;
+
+ case ErrorCode.OVERFLOW:
+ solvedata.error = /* Error displayed to user when they perform a bitwise operation on numbers greater than the current word */
+ _("Overflow. Try a bigger word size");
+ break;
+
+ case ErrorCode.UNKNOWN_VARIABLE:
+ solvedata.error = /* Error displayed to user when they an unknown variable is entered */
+ _("Unknown variable '%s'").printf (error_token);
+ break;
+
+ case ErrorCode.UNKNOWN_FUNCTION:
+ solvedata.error = /* Error displayed to user when an unknown function is entered */
+ _("Function '%s' is not defined").printf (error_token);
+ break;
+
+ case ErrorCode.UNKNOWN_CONVERSION:
+ solvedata.error = /* Error displayed to user when an conversion with unknown units is attempted */
+ _("Unknown conversion");
+ break;
+
+ case ErrorCode.MP:
+ if (mp_get_error () != null)
+ solvedata.error = mp_get_error ();
+ else if (error_token != null) /* Uncategorized error. Show error token to user */
+ solvedata.error = _("Malformed expression at token '%s'").printf (error_token);
+ else /* Unknown error. */
+ solvedata.error = _("Malformed expression");
+ break;
+
+ default:
+ solvedata.error = /* Error displayed to user when they enter an invalid calculation */
+ _("Malformed expression");
+ break;
+ }
+ queue.push (solvedata);
+
+ return null;
+ }
+
+ private bool show_in_progress ()
+ {
+ if (in_solve)
+ status = _("Calculating");
+ return false;
+ }
+
+ private bool look_for_answer ()
+ {
+ var result = queue.try_pop ();
+
+ if (result == null)
+ return true;
+
+ in_solve = false;
+
+ if (result.error == null)
+ status = "";
+
+ if (result.error != null)
+ status = result.error;
+ else if (result.number_result != null)
+ set_number (result.number_result);
+ else if (result.text_result != null)
+ set (result.text_result);
+
+ return false;
+ }
+
+ public void solve ()
+ {
+ // FIXME: should replace calculation or give error message
+ if (in_solve)
+ return;
+
+ if (is_empty ())
+ return;
+
+ /* If showing a result return to the equation that caused it */
+ // FIXME: Result may not be here due to solve (i.e. the user may have entered "ans")
+ if (is_result ())
+ {
+ undo ();
+ return;
+ }
+
+ in_solve = true;
+
+ set_number_mode (NumberMode.NORMAL);
+
+ new Thread<void*> ("", solve_real);
+
+ Timeout.add (50, look_for_answer);
+ Timeout.add (100, show_in_progress);
+ }
+
+ private void* factorize_real ()
+ {
+ var x = get_number ();
+ var factors = x.factorize ();
+
+ var text = "";
+ var i = 0;
+ foreach (var factor in factors)
+ {
+ if (i != 0)
+ text += "Ã";
+ text += serializer.to_string (factor);
+ i++;
+ }
+
+ var result = SolveData ();
+ result.text_result = text;
+ queue.push (result);
+
+ return null;
+ }
+
+ public void factorize ()
+ {
+ // FIXME: should replace calculation or give error message
+ if (in_solve)
+ return;
+
+ var x = get_number ();
+ if (x == null || !x.is_integer ())
+ {
+ /* Error displayed when trying to factorize a non-integer value */
+ status = _("Need an integer to factorize");
+ return;
+ }
+
+ in_solve = true;
+
+ new Thread<void*> ("", factorize_real);
+
+ Timeout.add (50, look_for_answer);
+ Timeout.add (100, show_in_progress);
+ }
+
+ public void delete_next ()
+ {
+ int cursor;
+ get ("cursor-position", out cursor, null);
+ if (cursor >= get_char_count ())
+ return;
+
+ Gtk.TextIter start, end;
+ get_iter_at_offset (out start, cursor);
+ get_iter_at_offset (out end, cursor+1);
+ @delete (ref start, ref end);
+ }
+
+ public new void backspace ()
+ {
+ /* Can't delete empty display */
+ if (is_empty ())
+ return;
+
+ if (get_has_selection ())
+ delete_selection (false, false);
+ else
+ {
+ Gtk.TextIter iter;
+ get_iter_at_mark (out iter, get_insert ());
+ (this as Gtk.TextBuffer).backspace (iter, true, true);
+ }
+ }
+
+ public void clear ()
+ {
+ set_number_mode (NumberMode.NORMAL);
+ set_text ("", -1);
+ clear_ans (false);
+ }
+
+ public void shift (int count)
+ {
+ var z = get_number ();
+ if (z == null)
+ {
+ /* This message is displayed in the status bar when a bit shift operation is performed and the display does not contain a number */
+ status = _("No sane value to bitwise shift");
+ return;
+ }
+
+ set_number (z.shift (count));
+ }
+
+ public void toggle_bit (uint bit)
+ {
+ var x = get_number ();
+ var max = new Number.unsigned_integer (uint64.MAX);
+ if (x == null || x.is_negative () || x.compare (max) > 0)
+ {
+ /* Message displayed when cannot toggle bit in display */
+ status = _("Displayed value not an integer");
+ return;
+ }
+
+ var bits = x.to_unsigned_integer ();
+ bits ^= (1LL << (63 - bit));
+ x = new Number.unsigned_integer (bits);
+
+ // FIXME: Only do this if in ans format, otherwise set text in same format as previous number
+ set_number (x);
+ }
+
+ private void pre_insert_text_cb (Gtk.TextIter location, string text, int len)
+ {
+ if (in_reformat)
+ return;
+
+ /* If following a delete then have already pushed undo stack (Gtk.TextBuffer
+ doesn't indicate replace operations so we have to infer them) */
+ if (!in_delete)
+ push_undo_stack ();
+
+ /* Clear result on next digit entered if cursor at end of line */
+ var c = text.get_char (0);
+ int cursor;
+ get ("cursor-position", out cursor, null);
+ if ((c.isdigit () || c == serializer.get_radix ()) && is_result () && cursor >= get_char_count ())
+ {
+ set_text ("", -1);
+ clear_ans (false);
+ get_end_iter (out location);
+ }
+
+ if (ans_start_mark != null)
+ {
+ var offset = location.get_offset ();
+ int ans_start, ans_end;
+ get_ans_offsets (out ans_start, out ans_end);
+
+ /* Inserted inside ans */
+ if (offset > ans_start && offset < ans_end)
+ clear_ans (true);
+ }
+ }
+
+ private bool on_delete ()
+ {
+ in_delete = false;
+ return false;
+ }
+
+ private void pre_delete_range_cb (Gtk.TextIter start, Gtk.TextIter end)
+ {
+ if (in_reformat)
+ return;
+
+ push_undo_stack ();
+
+ in_delete = true;
+ Idle.add (on_delete);
+
+ if (ans_start_mark != null)
+ {
+ var start_offset = start.get_offset ();
+ var end_offset = end.get_offset ();
+ int ans_start, ans_end;
+ get_ans_offsets (out ans_start, out ans_end);
+
+ /* Deleted part of ans */
+ if (start_offset < ans_end && end_offset > ans_start)
+ clear_ans (true);
+ }
+ }
+
+ private void insert_text_cb (Gtk.TextIter location, string text, int len)
+ {
+ if (in_reformat)
+ return;
+
+ state.entered_multiply = text == "Ã";
+
+ /* Update thousands separators */
+ reformat_separators ();
+
+ notify_property ("display");
+ }
+
+ private void delete_range_cb (Gtk.TextIter start, Gtk.TextIter end)
+ {
+ if (in_reformat)
+ return;
+
+ state.entered_multiply = false;
+
+ /* Update thousands separators */
+ reformat_separators ();
+
+ // FIXME: A replace will emit this both for delete-range and insert-text, can it be avoided?
+ notify_property ("display");
+ }
+}
+
+private class MEquation : Equation
+{
+ private MathEquation m_equation;
+
+ public MEquation (MathEquation m_equation, string equation)
+ {
+ base (equation);
+ this.m_equation = m_equation;
+ }
+
+ public override bool variable_is_defined (string name)
+ {
+ var lower_name = name.down ();
+
+ if (lower_name == "rand" || lower_name == "ans")
+ return true;
+
+ return m_equation.get_variables ().get (name) != null;
+ }
+
+ public override Number? get_variable (string name)
+ {
+ var lower_name = name.down ();
+
+ if (lower_name == "rand")
+ return new Number.random ();
+ else if (lower_name == "ans")
+ return m_equation.get_answer ();
+ else
+ return m_equation.get_variables ().get (name);
+ }
+
+ public override void set_variable (string name, Number x)
+ {
+ /* FIXME: Don't allow writing to built-in variables, e.g. ans, rand, sin, ... */
+ m_equation.get_variables ().set (name, x);
+ }
+
+ public override Number? convert (Number x, string x_units, string z_units)
+ {
+ return UnitManager.get_default ().convert_by_symbol (x, x_units, z_units);
+ }
+}
\ No newline at end of file
diff --git a/src/math-preferences.vala b/src/math-preferences.vala
new file mode 100644
index 0000000..66e66de
--- /dev/null
+++ b/src/math-preferences.vala
@@ -0,0 +1,301 @@
+/*
+ * Copyright (C) 2008-2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public class MathPreferencesDialog : Gtk.Dialog
+{
+ private MathEquation equation;
+ private Gtk.ComboBox angle_unit_combo;
+ private Gtk.ComboBox number_format_combo;
+ private Gtk.ComboBox word_size_combo;
+ private Gtk.SpinButton decimal_places_spin;
+ private Gtk.CheckButton thousands_separator_check;
+ private Gtk.CheckButton trailing_zeroes_check;
+
+ public MathPreferencesDialog (MathEquation equation)
+ {
+ this.equation = equation;
+
+ set_title (/* Title of preferences dialog */
+ _("Preferences"));
+ border_width = 8;
+ add_button (/* Label on close button in preferences dialog */
+ _("_Close"), 0);
+
+ var grid = new Gtk.Grid ();
+ grid.show ();
+ grid.border_width = 5;
+ grid.column_spacing = 6;
+ grid.row_spacing = 12;
+ get_content_area ().pack_start (grid, true, true, 0);
+
+ var label = new Gtk.Label.with_mnemonic (/* Preferences dialog: Label for number format combo box */
+ _("Number _Format:"));
+ label.show ();
+ label.xalign = 0;
+ grid.attach (label, 0, 0, 1, 1);
+
+ number_format_combo = new Gtk.ComboBox ();
+ label.mnemonic_widget = number_format_combo;
+ number_format_combo.show ();
+ number_format_combo.changed.connect (number_format_combo_changed_cb);
+ grid.attach (number_format_combo, 1, 0, 1, 1);
+
+ var model = new Gtk.ListStore (2, typeof (string), typeof (int));
+ number_format_combo.model = model;
+ Gtk.TreeIter iter;
+ model.append (out iter);
+ model.set (iter, 0,
+ /* Number display mode combo: Automatic, e.g. 1234 (or scientific for large number 1.234Ã10^99) */
+ _("Automatic"), 1, DisplayFormat.AUTOMATIC, -1);
+ model.append (out iter);
+ model.set (iter, 0,
+ /* Number display mode combo: Fixed, e.g. 1234 */
+ _("Fixed"), 1, DisplayFormat.FIXED, -1);
+ model.append (out iter);
+ model.set (iter, 0,
+ /* Number display mode combo: Scientific, e.g. 1.234Ã10^3 */
+ _("Scientific"), 1, DisplayFormat.SCIENTIFIC, -1);
+ model.append (out iter);
+ model.set (iter, 0,
+ /* Number display mode combo: Engineering, e.g. 1.234k */
+ _("Engineering"), 1, DisplayFormat.ENGINEERING, -1);
+ var renderer = new Gtk.CellRendererText ();
+ number_format_combo.pack_start (renderer, true);
+ number_format_combo.add_attribute (renderer, "text", 0);
+
+ var alignment = new Gtk.Alignment (0.5f, 0.5f, 1.0f, 1.0f);
+ alignment.bottom_padding = 6;
+ alignment.left_padding = 12;
+ alignment.show ();
+ grid.attach (alignment, 0, 1, 2, 1);
+
+ var format_options_box = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
+ format_options_box.show ();
+ alignment.add (format_options_box);
+
+ var places_box = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
+ places_box.show ();
+ format_options_box.pack_start (places_box, false, false, 0);
+
+ /* Label used in preferences dialog. The %d is replaced by a spinbutton */
+ var string = _("Show %d decimal _places");
+ var tokens = string.split ("%d", 2);
+
+ var decimal_places_adjustment = new Gtk.Adjustment (0.0, 0.0, 9.0, 1.0, 1.0, 0.0);
+ decimal_places_spin = new Gtk.SpinButton (decimal_places_adjustment, 0.0, 0);
+
+ if (tokens.length > 0)
+ {
+ label = new Gtk.Label.with_mnemonic (tokens[0].strip ());
+ label.mnemonic_widget = decimal_places_spin;
+ label.show ();
+ places_box.pack_start (label, false, false, 0);
+ }
+
+ decimal_places_spin.show ();
+ decimal_places_spin.value_changed.connect (decimal_places_spin_change_value_cb);
+ places_box.pack_start (decimal_places_spin, false, false, 0);
+
+ if (tokens.length == 2)
+ {
+ label = new Gtk.Label.with_mnemonic (tokens[1].strip ());
+ label.mnemonic_widget = decimal_places_spin;
+ label.show ();
+ places_box.pack_start (label, false, false, 0);
+ }
+
+ trailing_zeroes_check = new Gtk.CheckButton.with_mnemonic (/* Preferences dialog: label for show trailing zeroes check button */
+ _("Show trailing _zeroes"));
+ trailing_zeroes_check.show ();
+ trailing_zeroes_check.toggled.connect (trailing_zeroes_check_toggled_cb);
+ format_options_box.pack_start (trailing_zeroes_check, false, false, 0);
+
+ thousands_separator_check = new Gtk.CheckButton.with_mnemonic (/* Preferences dialog: label for show show thousands separator check button */
+ _("Show _thousands separators"));
+ thousands_separator_check.show ();
+ thousands_separator_check.toggled.connect (thousands_separator_check_toggled_cb);
+ format_options_box.pack_start (thousands_separator_check, false, false, 0);
+
+ label = new Gtk.Label.with_mnemonic (/* Preferences dialog: Label for angle unit combo box */
+ _("_Angle units:"));
+ label.show ();
+ label.xalign = 0;
+ grid.attach (label, 0, 2, 1, 1);
+
+ angle_unit_combo = new Gtk.ComboBox ();
+ label.mnemonic_widget = angle_unit_combo;
+ angle_unit_combo.show ();
+ angle_unit_combo.changed.connect (angle_unit_combo_changed_cb);
+ grid.attach (angle_unit_combo, 1, 2, 1, 1);
+
+ model = new Gtk.ListStore (2, typeof (string), typeof (int));
+ angle_unit_combo.model = model;
+ model.append (out iter);
+ model.set (iter, 0,
+ /* Preferences dialog: Angle unit combo box: Use degrees for trigonometric calculations */
+ _("Degrees"), 1, AngleUnit.DEGREES, -1);
+ model.append (out iter);
+ model.set (iter, 0,
+ /* Preferences dialog: Angle unit combo box: Use radians for trigonometric calculations */
+ _("Radians"), 1, AngleUnit.RADIANS, -1);
+ model.append (out iter);
+ model.set (iter, 0,
+ /* Preferences dialog: Angle unit combo box: Use gradians for trigonometric calculations */
+ _("Gradians"), 1, AngleUnit.GRADIANS, -1);
+ renderer = new Gtk.CellRendererText ();
+ angle_unit_combo.pack_start (renderer, true);
+ angle_unit_combo.add_attribute (renderer, "text", 0);
+
+ label = new Gtk.Label.with_mnemonic (/* Preferences dialog: Label for word size combo box */
+ _("Word _size:"));
+ label.show ();
+ label.xalign = 0;
+ grid.attach (label, 0, 3, 1, 1);
+
+ word_size_combo = new Gtk.ComboBox ();
+ label.mnemonic_widget = word_size_combo;
+ word_size_combo.show ();
+ word_size_combo.changed.connect (word_size_combo_changed_cb);
+ grid.attach (word_size_combo, 1, 3, 1, 1);
+
+ model = new Gtk.ListStore (2, typeof (string), typeof (int));
+ word_size_combo.model = model;
+ model.append (out iter);
+ model.set (iter, 0, /* Word size combo: 8 bits */ _("8 bits"), 1, 8);
+ model.append (out iter);
+ model.set (iter, 0, /* Word size combo: 16 bits */ _("16 bits"), 1, 16);
+ model.append (out iter);
+ model.set (iter, 0, /* Word size combo: 32 bits */ _("32 bits"), 1, 32);
+ model.append (out iter);
+ model.set (iter, 0, /* Word size combo: 64 bits */ _("64 bits"), 1, 64);
+ renderer = new Gtk.CellRendererText ();
+ word_size_combo.pack_start (renderer, true);
+ word_size_combo.add_attribute (renderer, "text", 0);
+
+ equation.notify["accuracy"].connect ((pspec) => { accuracy_cb (); });
+ equation.notify["show-thousands-separators"].connect ((pspec) => { show_thousands_separators_cb (); });
+ equation.notify["show-trailing_zeroes"].connect ((pspec) => { show_trailing_zeroes_cb (); });
+ equation.notify["number-format"].connect ((pspec) => { number_format_cb (); });
+ equation.notify["word-size"].connect ((pspec) => { word_size_cb (); });
+ equation.notify["angle-units"].connect ((pspec) => { angle_unit_cb (); });
+
+ accuracy_cb ();
+ show_thousands_separators_cb ();
+ show_trailing_zeroes_cb ();
+ number_format_cb ();
+ word_size_cb ();
+ angle_unit_cb ();
+ }
+
+ protected override void response (int id)
+ {
+ hide ();
+ }
+
+ protected override bool delete_event (Gdk.EventAny event)
+ {
+ hide ();
+ return true;
+ }
+
+ private void number_format_combo_changed_cb (Gtk.ComboBox combo)
+ {
+ Gtk.TreeIter iter;
+ combo.get_active_iter (out iter);
+ DisplayFormat value;
+ combo.model.get (iter, 1, out value, -1);
+ equation.set_number_format (value);
+ }
+
+ private void angle_unit_combo_changed_cb (Gtk.ComboBox combo)
+ {
+ Gtk.TreeIter iter;
+ combo.get_active_iter (out iter);
+ AngleUnit value;
+ combo.model.get (iter, 1, out value, -1);
+ equation.set_angle_units (value);
+ }
+
+ private void word_size_combo_changed_cb (Gtk.ComboBox combo)
+ {
+ Gtk.TreeIter iter;
+ combo.get_active_iter (out iter);
+ int value;
+ combo.model.get (iter, 1, out value, -1);
+ equation.word_size = value;
+ }
+
+ private void decimal_places_spin_change_value_cb (Gtk.SpinButton spin)
+ {
+ var value = spin.get_value_as_int ();
+ equation.set_accuracy (value);
+ }
+
+ private void thousands_separator_check_toggled_cb (Gtk.ToggleButton check)
+ {
+ var value = check.get_active ();
+ equation.set_show_thousands_separators (value);
+ }
+
+ private void trailing_zeroes_check_toggled_cb (Gtk.ToggleButton check)
+ {
+ var value = check.get_active ();
+ equation.set_show_trailing_zeroes (value);
+ }
+
+ private void accuracy_cb ()
+ {
+ decimal_places_spin.set_value (equation.get_accuracy ());
+ }
+
+ private void show_thousands_separators_cb ()
+ {
+ thousands_separator_check.set_active (equation.get_show_thousands_separators ());
+ }
+
+ private void show_trailing_zeroes_cb ()
+ {
+ trailing_zeroes_check.set_active (equation.get_show_trailing_zeroes ());
+ }
+
+ private void number_format_cb ()
+ {
+ set_combo_box_from_int (number_format_combo, equation.get_number_format ());
+ }
+
+ private void word_size_cb ()
+ {
+ set_combo_box_from_int (word_size_combo, equation.word_size);
+ }
+
+ private void angle_unit_cb ()
+ {
+ set_combo_box_from_int (angle_unit_combo, equation.get_angle_units ());
+ }
+
+ private void set_combo_box_from_int (Gtk.ComboBox combo, int value)
+ {
+ Gtk.TreeIter iter;
+ var valid = combo.model.get_iter_first (out iter);
+ while (valid)
+ {
+ int v;
+
+ combo.model.get (iter, 1, out v, -1);
+ if (v == value)
+ break;
+ valid = combo.model.iter_next (ref iter);
+ }
+ if (!valid)
+ valid = combo.model.get_iter_first (out iter);
+
+ combo.set_active_iter (iter);
+ }
+}
diff --git a/src/math-variable-popup.vala b/src/math-variable-popup.vala
new file mode 100644
index 0000000..794fed7
--- /dev/null
+++ b/src/math-variable-popup.vala
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2008-2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public class MathVariablePopup : Gtk.Window
+{
+ private MathEquation equation;
+
+ private Gtk.Box vbox;
+ private Gtk.Entry variable_name_entry;
+ private Gtk.Button add_variable_button;
+
+ public MathVariablePopup (MathEquation equation)
+ {
+ this.equation = equation;
+
+ decorated = false;
+ skip_taskbar_hint = true;
+ border_width = 6;
+
+ /* Destroy this window when it loses focus */
+ focus_out_event.connect ((event) => { destroy (); return false; });
+
+ vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
+ vbox.homogeneous = true;
+ add (vbox);
+ vbox.show ();
+
+ var names = equation.get_variables ().get_names ();
+ for (var i = 0; names[i] != null; i++)
+ {
+ var value = equation.get_variables ().get (names[i]);
+ var entry = make_variable_entry (names[i], value, true);
+ entry.show ();
+ vbox.pack_start (entry, false, true, 0);
+ }
+
+ var entry = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
+ entry.show ();
+
+ // TODO: Show greyed "variable name" text to give user a hint how to use
+ variable_name_entry = new Gtk.Entry ();
+ variable_name_entry.key_press_event.connect (variable_name_key_press_cb);
+ variable_name_entry.changed.connect (variable_name_changed_cb);
+ variable_name_entry.activate.connect (add_variable_cb);
+ entry.pack_start (variable_name_entry, true, true, 0);
+ variable_name_entry.show ();
+
+ add_variable_button = new Gtk.Button ();
+ add_variable_button.sensitive = false;
+ add_variable_button.clicked.connect (add_variable_cb);
+ var image = new Gtk.Image.from_stock (Gtk.Stock.ADD, Gtk.IconSize.BUTTON);
+ add_variable_button.add (image);
+ entry.pack_start (add_variable_button, false, true, 0);
+ image.show ();
+ add_variable_button.show ();
+ vbox.pack_end (entry, false, true, 0);
+
+ entry = make_variable_entry ("rand", null, false);
+ entry.show ();
+ vbox.pack_end (entry, false, true, 0);
+
+ entry = make_variable_entry ("ans", equation.get_answer (), false);
+ entry.show ();
+ vbox.pack_end (entry, false, true, 0);
+ }
+
+ private void insert_variable_cb (Gtk.Widget widget)
+ {
+ var name = widget.get_data<string> ("variable_name");
+ equation.insert (name);
+
+ widget.get_toplevel ().destroy ();
+ }
+
+ private bool variable_name_key_press_cb (Gtk.Widget widget, Gdk.EventKey event)
+ {
+ /* Can't have whitespace in names, so replace with underscores */
+ if (event.keyval == Gdk.Key.space || event.keyval == Gdk.Key.KP_Space)
+ event.keyval = Gdk.Key.underscore;
+
+ return false;
+ }
+
+ private void variable_name_changed_cb ()
+ {
+ add_variable_button.sensitive = variable_name_entry.get_text () != "";
+ }
+
+ private void add_variable_cb (Gtk.Widget widget)
+ {
+ var name = variable_name_entry.get_text ();
+ if (name == "")
+ return;
+
+ var z = equation.get_number ();
+ if (z != null)
+ equation.get_variables ().set (name, z);
+ else if (equation.is_result ())
+ equation.get_variables ().set (name, equation.get_answer ());
+ else
+ warning ("Can't add variable %s, the display is not a number", name);
+
+ widget.get_toplevel ().destroy ();
+ }
+
+ private void save_variable_cb (Gtk.Widget widget)
+ {
+ var name = widget.get_data<string> ("variable_name");
+ var z = equation.get_number ();
+ if (z != null)
+ equation.get_variables ().set (name, z);
+ else if (equation.is_result ())
+ equation.get_variables ().set (name, equation.get_answer ());
+ else
+ warning ("Can't save variable %s, the display is not a number", name);
+
+ widget.get_toplevel ().destroy ();
+ }
+
+ private void delete_variable_cb (Gtk.Widget widget)
+ {
+ var name = widget.get_data<string> ("variable_name");
+ equation.get_variables ().delete (name);
+
+ widget.get_toplevel ().destroy ();
+ }
+
+ private Gtk.Box make_variable_entry (string name, Number? value, bool writable)
+ {
+ var hbox = new Gtk.Box (Gtk.Orientation.HORIZONTAL, 6);
+
+ string text;
+ if (value != null)
+ {
+ var value_text = equation.get_serializer ().to_string (value);
+ text = "<b>%s</b> = %s".printf (name, value_text);
+ }
+ else
+ text = "<b>%s</b>".printf (name);
+
+ var button = new Gtk.Button ();
+ button.set_data<string> ("variable_name", name);
+ button.clicked.connect (insert_variable_cb);
+ button.set_relief (Gtk.ReliefStyle.NONE);
+ hbox.pack_start (button, true, true, 0);
+ button.show ();
+
+ var label = new Gtk.Label (text);
+ label.set_use_markup (true);
+ label.set_alignment (0.0f, 0.5f);
+ button.add (label);
+ label.show ();
+
+ if (writable)
+ {
+ button = new Gtk.Button ();
+ button.set_data<string> ("variable_name", name);
+ var image = new Gtk.Image.from_stock (Gtk.Stock.SAVE, Gtk.IconSize.BUTTON);
+ button.add (image);
+ hbox.pack_start (button, false, true, 0);
+ button.clicked.connect (save_variable_cb);
+ image.show ();
+ button.show ();
+
+ button = new Gtk.Button ();
+ button.set_data<string> ("variable_name", name);
+ image = new Gtk.Image.from_stock (Gtk.Stock.DELETE, Gtk.IconSize.BUTTON);
+ button.add (image);
+ hbox.pack_start (button, false, true, 0);
+ button.clicked.connect (delete_variable_cb);
+ image.show ();
+ button.show ();
+ }
+
+ return hbox;
+ }
+}
diff --git a/src/math-variables.vala b/src/math-variables.vala
new file mode 100644
index 0000000..f326f1f
--- /dev/null
+++ b/src/math-variables.vala
@@ -0,0 +1,114 @@
+/*
+ * Copyright (C) 2008-2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public class MathVariables : Object
+{
+ private string file_name;
+ private HashTable<string, Number?> registers;
+ private Serializer serializer;
+
+ public MathVariables ()
+ {
+ registers = new HashTable <string, Number?> (str_hash, str_equal);
+ file_name = Path.build_filename (Environment.get_user_data_dir (), "gcalctool", "registers");
+ serializer = new Serializer (DisplayFormat.SCIENTIFIC, 10, 50);
+ serializer.set_radix ('.');
+ registers_load ();
+ }
+
+ private void registers_load ()
+ {
+ string data;
+ try
+ {
+ FileUtils.get_contents (file_name, out data);
+ }
+ catch (FileError e)
+ {
+ return;
+ }
+
+ registers.remove_all ();
+
+ var lines = data.split ("\n");
+ foreach (var line in lines)
+ {
+ var i = line.index_of_char ('=');
+ if (i < 0)
+ continue;
+
+ var name = line.substring (0, i).strip ();
+ var value = line.substring (i+1).strip ();
+
+ var t = mp_set_from_string (value);
+ if (t != null)
+ registers.insert (name, t);
+ }
+ }
+
+ private void save ()
+ {
+ var data = "";
+ var iter = HashTableIter<string, Number?> (registers);
+ string name;
+ Number? value;
+ while (iter.next (out name, out value))
+ {
+ var number = serializer.to_string (value);
+ data += "%s=%s\n".printf (name, number);
+ }
+
+ var dir = Path.get_dirname (file_name);
+ DirUtils.create_with_parents (dir, 0700);
+ try
+ {
+ FileUtils.set_contents (file_name, data);
+ }
+ catch (FileError e)
+ {
+ }
+ }
+
+ // FIXME: Sort
+ public string[] get_names ()
+ {
+ var names = new string[registers.size () + 1];
+
+ var iter = HashTableIter<string, Number?> (registers);
+ var i = 0;
+ string name;
+ Number? value;
+ while (iter.next (out name, out value))
+ {
+ names[i] = name;
+ i++;
+ }
+ names[i] = null;
+
+ return names;
+ }
+
+ public new void set (string name, Number value)
+ {
+ registers.insert (name, value);
+ save ();
+ }
+
+ public new Number? get (string name)
+ {
+ return registers.lookup (name);
+ }
+
+ public void delete (string name)
+ {
+ registers.remove (name);
+ save ();
+ }
+}
diff --git a/src/math-window.vala b/src/math-window.vala
new file mode 100644
index 0000000..1c3a614
--- /dev/null
+++ b/src/math-window.vala
@@ -0,0 +1,117 @@
+/*
+ * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (C) 2008-2012 Robert Ancell.
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public class MathWindow : Gtk.ApplicationWindow
+{
+ private MathEquation _equation;
+ public MathEquation equation { get { return _equation; } }
+
+ private MathDisplay _display;
+ public MathDisplay display { get { return _display; } }
+
+ private MathButtons _buttons;
+ public MathButtons buttons { get { return _buttons; } }
+ private bool right_aligned;
+
+ public MathWindow (Gtk.Application app, MathEquation equation)
+ {
+ Object (application: app);
+ _equation = equation;
+ set_title (/* Title of main window */
+ _("Calculator"));
+ role = "gcalctool";
+ resizable = false;
+
+ var main_vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 0);
+ add (main_vbox);
+ main_vbox.show ();
+
+ var vbox = new Gtk.Box (Gtk.Orientation.VERTICAL, 6);
+ vbox.border_width = 6;
+ main_vbox.pack_start (vbox, true, true, 0);
+ vbox.show ();
+
+ var scrolled_window = new Gtk.ScrolledWindow (null, null);
+ scrolled_window.set_policy (Gtk.PolicyType.AUTOMATIC, Gtk.PolicyType.NEVER);
+ scrolled_window.set_shadow_type (Gtk.ShadowType.IN);
+ vbox.pack_start (scrolled_window, false, false, 0);
+ scrolled_window.get_hadjustment ().changed.connect (scroll_changed_cb);
+ scrolled_window.get_hadjustment ().value_changed.connect (scroll_value_changed_cb);
+ right_aligned = true;
+ scrolled_window.show ();
+
+ _display = new MathDisplay (equation);
+ scrolled_window.add (display);
+ display.show ();
+
+ _buttons = new MathButtons (equation);
+ vbox.pack_start (buttons, true, true, 0);
+ buttons.show ();
+ }
+
+ public void critical_error (string title, string contents)
+ {
+ var dialog = new Gtk.MessageDialog (null, 0,
+ Gtk.MessageType.ERROR,
+ Gtk.ButtonsType.NONE,
+ "%s", title);
+ dialog.format_secondary_text ("%s", contents);
+ dialog.add_buttons (Gtk.Stock.QUIT, Gtk.ResponseType.ACCEPT);
+
+ dialog.run ();
+
+ destroy ();
+ }
+
+ protected override bool key_press_event (Gdk.EventKey event)
+ {
+ var result = base.key_press_event (event);
+
+ if (buttons.mode == ButtonMode.PROGRAMMING && (event.state & Gdk.ModifierType.CONTROL_MASK) == Gdk.ModifierType.CONTROL_MASK)
+ {
+ switch (event.keyval)
+ {
+ /* Binary */
+ case Gdk.Key.b:
+ equation.number_base = 2;
+ return true;
+ /* Octal */
+ case Gdk.Key.o:
+ equation.number_base = 8;
+ return true;
+ /* Decimal */
+ case Gdk.Key.d:
+ equation.number_base = 10;
+ return true;
+ /* Hexdecimal */
+ case Gdk.Key.h:
+ equation.number_base = 16;
+ return true;
+ }
+ }
+
+ return result;
+ }
+
+ private void scroll_changed_cb (Gtk.Adjustment adjustment)
+ {
+ if (right_aligned)
+ adjustment.set_value (adjustment.get_upper () - adjustment.get_page_size ());
+ }
+
+ private void scroll_value_changed_cb (Gtk.Adjustment adjustment)
+ {
+ if (adjustment.get_value () == adjustment.get_upper () - adjustment.get_page_size ())
+ right_aligned = true;
+ else
+ right_aligned = false;
+ }
+}
\ No newline at end of file
diff --git a/src/number.vala b/src/number.vala
new file mode 100644
index 0000000..82000b9
--- /dev/null
+++ b/src/number.vala
@@ -0,0 +1,3223 @@
+/*
+ * Copyright (C) 1987-2008 Sun Microsystems, Inc. All Rights Reserved.
+ * Copyright (C) 2008-2012 Robert Ancell
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+/* This maths library is based on the MP multi-precision floating-point
+ * arithmetic package originally written in FORTRAN by Richard Brent,
+ * Computer Centre, Australian National University in the 1970's.
+ *
+ * It has been converted from FORTRAN into C using the freely available
+ * f2c translator, available via netlib on research.att.com.
+ *
+ * The subsequently converted C code has then been tidied up, mainly to
+ * remove any dependencies on the libI77 and libF77 support libraries.
+ *
+ * FOR A GENERAL DESCRIPTION OF THE PHILOSOPHY AND DESIGN OF MP,
+ * SEE - R. P. BRENT, A FORTRAN MULTIPLE-PRECISION ARITHMETIC
+ * PACKAGE, ACM TRANS. MATH. SOFTWARE 4 (MARCH 1978), 57-70.
+ * SOME ADDITIONAL DETAILS ARE GIVEN IN THE SAME ISSUE, 71-81.
+ * FOR DETAILS OF THE IMPLEMENTATION, CALLING SEQUENCES ETC. SEE
+ * THE MP USERS GUIDE.
+ */
+
+/* Size of the multiple precision values */
+private const int SIZE = 1000;
+
+/* Base for numbers */
+private const int BASE = 10000;
+
+//2E0 BELOW ENSURES AT LEAST ONE GUARD DIGIT
+//MP.t = (int) ((float) (accuracy) * Math.log ((float)10.) / Math.log ((float) BASE) + (float) 2.0);
+//if (MP.t > SIZE)
+//{
+// mperr ("SIZE TOO SMALL IN CALL TO MPSET, INCREASE SIZE AND DIMENSIONS OF MP ARRAYS TO AT LEAST %d ***", MP.t);
+// MP.t = SIZE;
+//}
+private const int T = 100;
+
+private delegate int BitwiseFunc (int v1, int v2);
+
+public enum AngleUnit
+{
+ RADIANS,
+ DEGREES,
+ GRADIANS
+}
+
+/* Object for a high precision floating point number representation
+ *
+ * x = sign * (BASE^(exponent-1) + BASE^(exponent-2) + ...)
+ */
+public class Number
+{
+ /* Sign (+1, -1) or 0 for the value zero */
+ public int re_sign;
+ public int im_sign;
+
+ /* Exponent (to base BASE) */
+ public int re_exponent;
+ public int im_exponent;
+
+ /* Normalized fraction */
+ public int re_fraction[1000]; // SIZE
+ public int im_fraction[1000]; // SIZE
+
+ public Number.integer (int64 value)
+ {
+ if (value < 0)
+ {
+ value = -value;
+ re_sign = -1;
+ }
+ else if (value > 0)
+ re_sign = 1;
+ else
+ re_sign = 0;
+
+ while (value != 0)
+ {
+ re_fraction[re_exponent] = (int) (value % BASE);
+ re_exponent++;
+ value /= BASE;
+ }
+ for (var i = 0; i < re_exponent / 2; i++)
+ {
+ int t = re_fraction[i];
+ re_fraction[i] = re_fraction[re_exponent - i - 1];
+ re_fraction[re_exponent - i - 1] = t;
+ }
+ }
+
+ public Number.unsigned_integer (uint64 x)
+ {
+ if (x == 0)
+ re_sign = 0;
+ else
+ re_sign = 1;
+
+ while (x != 0)
+ {
+ re_fraction[re_exponent] = (int) (x % BASE);
+ x = x / BASE;
+ re_exponent++;
+ }
+ for (var i = 0; i < re_exponent / 2; i++)
+ {
+ int t = re_fraction[i];
+ re_fraction[i] = re_fraction[re_exponent - i - 1];
+ re_fraction[re_exponent - i - 1] = t;
+ }
+ }
+
+ public Number.fraction (int64 numerator, int64 denominator)
+ {
+ mp_gcd (ref numerator, ref denominator);
+
+ if (denominator < 0)
+ {
+ numerator = -numerator;
+ denominator = -denominator;
+ }
+
+ Number.integer (numerator);
+ if (denominator != 1)
+ {
+ var z = divide_integer (denominator);
+ re_sign = z.re_sign;
+ im_sign = z.im_sign;
+ re_exponent = z.re_exponent;
+ im_exponent = z.im_exponent;
+ for (var i = 0; i < z.re_fraction.length; i++)
+ {
+ re_fraction[i] = z.re_fraction[i];
+ im_fraction[i] = z.im_fraction[i];
+ }
+ }
+ }
+
+ public Number.float (float value)
+ {
+ var z = new Number.integer (0);
+
+ if (value != 0)
+ {
+ /* CHECK SIGN */
+ var rj = 0f;
+ if (value < 0.0f)
+ {
+ z.re_sign = -1;
+ rj = -value;
+ }
+ else if (value > 0.0f)
+ {
+ z.re_sign = 1;
+ rj = value;
+ }
+
+ /* INCREASE IE AND DIVIDE RJ BY 16. */
+ var ie = 0;
+ while (rj >= 1.0f)
+ {
+ ie++;
+ rj *= 0.0625f;
+ }
+ while (rj < 0.0625f)
+ {
+ ie--;
+ rj *= 16.0f;
+ }
+
+ /* NOW RJ IS DY DIVIDED BY SUITABLE POWER OF 16.
+ * SET re_exponent TO 0
+ */
+ z.re_exponent = 0;
+
+ /* CONVERSION LOOP (ASSUME SINGLE-PRECISION OPS. EXACT) */
+ for (var i = 0; i < T + 4; i++)
+ {
+ rj *= BASE;
+ z.re_fraction[i] = (int) rj;
+ rj -= z.re_fraction[i];
+ }
+
+ /* NORMALIZE RESULT */
+ mp_normalize (ref z);
+
+ /* Computing MAX */
+ var ib = int.max (BASE * 7 * BASE, 32767) / 16;
+ var tp = 1;
+
+ /* NOW MULTIPLY BY 16**IE */
+ if (ie < 0)
+ {
+ var k = -ie;
+ for (var i = 1; i <= k; ++i)
+ {
+ tp <<= 4;
+ if (tp <= ib && tp != BASE && i < k)
+ continue;
+ z = z.divide_integer (tp);
+ tp = 1;
+ }
+ }
+ else if (ie > 0)
+ {
+ for (var i = 1; i <= ie; ++i)
+ {
+ tp <<= 4;
+ if (tp <= ib && tp != BASE && i < ie)
+ continue;
+ z = z.multiply_integer (tp);
+ tp = 1;
+ }
+ }
+ }
+
+ re_sign = z.re_sign;
+ im_sign = z.im_sign;
+ re_exponent = z.re_exponent;
+ im_exponent = z.im_exponent;
+ for (var i = 0; i < z.re_fraction.length; i++)
+ {
+ re_fraction[i] = z.re_fraction[i];
+ im_fraction[i] = z.im_fraction[i];
+ }
+ }
+
+ public Number.double (double value)
+ {
+ var z = new Number.integer (0);
+
+ if (value != 0)
+ {
+ /* CHECK SIGN */
+ var dj = 0.0;
+ if (value < 0.0)
+ {
+ z.re_sign = -1;
+ dj = -value;
+ }
+ else if (value > 0.0)
+ {
+ z.re_sign = 1;
+ dj = value;
+ }
+
+ /* INCREASE IE AND DIVIDE DJ BY 16. */
+ var ie = 0;
+ for (ie = 0; dj >= 1.0; ie++)
+ dj *= 1.0/16.0;
+
+ for ( ; dj < 1.0/16.0; ie--)
+ dj *= 16.0;
+
+ /* NOW DJ IS DY DIVIDED BY SUITABLE POWER OF 16
+ * SET re_exponent TO 0
+ */
+ z.re_exponent = 0;
+
+ /* CONVERSION LOOP (ASSUME DOUBLE-PRECISION OPS. EXACT) */
+ for (var i = 0; i < T + 4; i++)
+ {
+ dj *= (double) BASE;
+ z.re_fraction[i] = (int) dj;
+ dj -= (double) z.re_fraction[i];
+ }
+
+ /* NORMALIZE RESULT */
+ mp_normalize (ref z);
+
+ /* Computing MAX */
+ var ib = int.max (BASE * 7 * BASE, 32767) / 16;
+ var tp = 1;
+
+ /* NOW MULTIPLY BY 16**IE */
+ if (ie < 0)
+ {
+ var k = -ie;
+ for (var i = 1; i <= k; ++i)
+ {
+ tp <<= 4;
+ if (tp <= ib && tp != BASE && i < k)
+ continue;
+ z = z.divide_integer (tp);
+ tp = 1;
+ }
+ }
+ else if (ie > 0)
+ {
+ for (var i = 1; i <= ie; ++i)
+ {
+ tp <<= 4;
+ if (tp <= ib && tp != BASE && i < ie)
+ continue;
+ z = z.multiply_integer (tp);
+ tp = 1;
+ }
+ }
+ }
+
+ re_sign = z.re_sign;
+ im_sign = z.im_sign;
+ re_exponent = z.re_exponent;
+ im_exponent = z.im_exponent;
+ for (var i = 0; i < z.re_fraction.length; i++)
+ {
+ re_fraction[i] = z.re_fraction[i];
+ im_fraction[i] = z.im_fraction[i];
+ }
+ }
+
+ public Number.complex (Number x, Number y)
+ {
+ re_sign = x.re_sign;
+ re_exponent = x.re_exponent;
+ for (var i = 0; i < im_fraction.length; i++)
+ re_fraction[i] = x.re_fraction[i];
+
+ im_sign = y.re_sign;
+ im_exponent = y.re_exponent;
+ for (var i = 0; i < im_fraction.length; i++)
+ im_fraction[i] = y.re_fraction[i];
+ }
+
+ public Number.polar (Number r, Number theta, AngleUnit unit = AngleUnit.RADIANS)
+ {
+ var x = theta.cos (unit);
+ var y = theta.sin (unit);
+ Number.complex (x.multiply (r), y.multiply (r));
+ }
+
+ public Number.eulers ()
+ {
+ var z = new Number.integer (1).epowy ();
+ re_sign = z.re_sign;
+ im_sign = z.im_sign;
+ re_exponent = z.re_exponent;
+ im_exponent = z.im_exponent;
+ for (var i = 0; i < z.re_fraction.length; i++)
+ {
+ re_fraction[i] = z.re_fraction[i];
+ im_fraction[i] = z.im_fraction[i];
+ }
+ }
+
+ public Number.i ()
+ {
+ im_sign = 1;
+ im_exponent = 1;
+ im_fraction[0] = 1;
+ }
+
+ public Number.pi ()
+ {
+ // FIXME: Should generate PI to required accuracy
+ Number.double (Math.PI);
+ }
+
+ /* Sets z to be a uniform random number in the range [0, 1] */
+ public Number.random ()
+ {
+ Number.double (Random.next_double ());
+ }
+
+ public int64 to_integer ()
+ {
+ int64 z = 0;
+
+ /* |x| <= 1 */
+ if (re_sign == 0 || re_exponent <= 0)
+ return 0;
+
+ /* Multiply digits together */
+ for (var i = 0; i < re_exponent; i++)
+ {
+ var t = z;
+ z = z * BASE + re_fraction[i];
+
+ /* Check for overflow */
+ if (z <= t)
+ return 0;
+ }
+
+ /* Validate result */
+ var v = z;
+ for (var i = re_exponent - 1; i >= 0; i--)
+ {
+ /* Get last digit */
+ var digit = v - (v / BASE) * BASE;
+ if (re_fraction[i] != digit)
+ return 0;
+
+ v /= BASE;
+ }
+ if (v != 0)
+ return 0;
+
+ return re_sign * z;
+ }
+
+ public uint64 to_unsigned_integer ()
+ {
+ /* x <= 1 */
+ if (re_sign <= 0 || re_exponent <= 0)
+ return 0;
+
+ /* Multiply digits together */
+ uint64 z = 0;
+ for (var i = 0; i < re_exponent; i++)
+ {
+ var t = z;
+ z = z * BASE + re_fraction[i];
+
+ /* Check for overflow */
+ if (z <= t)
+ return 0;
+ }
+
+ /* Validate result */
+ var v = z;
+ for (var i = re_exponent - 1; i >= 0; i--)
+ {
+ /* Get last digit */
+ var digit = (uint64) v - (v / BASE) * BASE;
+ if (re_fraction[i] != digit)
+ return 0;
+
+ v /= BASE;
+ }
+ if (v != 0)
+ return 0;
+
+ return z;
+ }
+
+ public float to_float ()
+ {
+ if (is_zero ())
+ return 0f;
+
+ var z = 0f;
+ for (var i = 0; i < T; i++)
+ {
+ if (re_fraction[i] != 0)
+ z += re_fraction[i] * Math.powf (BASE, re_exponent - i - 1);
+ }
+
+ if (re_sign < 0)
+ return -z;
+ else
+ return z;
+ }
+
+ public double to_double ()
+ {
+ if (is_zero ())
+ return 0d;
+
+ var z = 0d;
+ for (var i = 0; i < T; i++)
+ {
+ if (re_fraction[i] != 0)
+ z += re_fraction[i] * Math.pow (BASE, re_exponent - i - 1);
+ }
+
+ if (re_sign < 0)
+ return -z;
+ else
+ return z;
+ }
+
+ /* Return true if the value is x == 0 */
+ public bool is_zero ()
+ {
+ return re_sign == 0 && im_sign == 0;
+ }
+
+ /* Return true if x < 0 */
+ public bool is_negative ()
+ {
+ return re_sign < 0;
+ }
+
+ /* Return true if x is integer */
+ public bool is_integer ()
+ {
+ if (is_complex ())
+ return false;
+
+ /* This fix is required for 1/3 repiprocal not being detected as an integer */
+ /* Multiplication and division by 10000 is used to get around a
+ * limitation to the "fix" for Sun bugtraq bug #4006391 in the
+ * floor () routine in mp.c, when the re_exponent is less than 1.
+ */
+ var t3 = new Number.integer (10000);
+ var t1 = multiply (t3);
+ t1 = t1.divide (t3);
+ var t2 = t1.floor ();
+ return t1.equals (t2);
+
+ /* Correct way to check for integer */
+ /*
+
+ // Zero is an integer
+ if (is_zero ())
+ return true;
+
+ // fractional
+ if (re_exponent <= 0)
+ return false;
+
+ // Look for fractional components
+ for (var i = re_exponent; i < SIZE; i++)
+ {
+ if (re_fraction[i] != 0)
+ return false;
+ }
+
+ return true;*/
+ }
+
+ /* Return true if x is a positive integer */
+ public bool is_positive_integer ()
+ {
+ if (is_complex ())
+ return false;
+ else
+ return re_sign >= 0 && is_integer ();
+ }
+
+ /* Return true if x is a natural number (an integer â 0) */
+ public bool is_natural ()
+ {
+ if (is_complex ())
+ return false;
+ else
+ return re_sign > 0 && is_integer ();
+ }
+
+ /* Return true if x has an imaginary component */
+ public bool is_complex ()
+ {
+ return im_sign != 0;
+ }
+
+ /* Return true if x == y */
+ public bool equals (Number y)
+ {
+ return compare (y) == 0;
+ }
+
+ /* Returns:
+ * 0 if x == y
+ * <0 if x < y
+ * >0 if x > y
+ */
+ public int compare (Number y)
+ {
+ if (re_sign != y.re_sign)
+ {
+ if (re_sign > y.re_sign)
+ return 1;
+ else
+ return -1;
+ }
+
+ /* x = y = 0 */
+ if (is_zero ())
+ return 0;
+
+ /* See if numbers are of different magnitude */
+ if (re_exponent != y.re_exponent)
+ {
+ if (re_exponent > y.re_exponent)
+ return re_sign;
+ else
+ return -re_sign;
+ }
+
+ /* Compare fractions */
+ for (var i = 0; i < SIZE; i++)
+ {
+ if (re_fraction[i] == y.re_fraction[i])
+ continue;
+
+ if (re_fraction[i] > y.re_fraction[i])
+ return re_sign;
+ else
+ return -re_sign;
+ }
+
+ /* x = y */
+ return 0;
+ }
+
+ /* Sets z = sgn (x) */
+ public Number sgn ()
+ {
+ if (is_zero ())
+ return new Number.integer (0);
+ else if (is_negative ())
+ return new Number.integer (-1);
+ else
+ return new Number.integer (1);
+ }
+
+ /* Sets z = âx */
+ public Number invert_sign ()
+ {
+ var z = copy ();
+
+ z.re_sign = -z.re_sign;
+ z.im_sign = -z.im_sign;
+
+ return z;
+ }
+
+ /* Sets z = |x| */
+ public Number abs ()
+ {
+ if (is_complex ())
+ {
+ var x_real = real_component ();
+ var x_im = imaginary_component ();
+
+ x_real = x_real.multiply (x_real);
+ x_im = x_im.multiply (x_im);
+ var z = x_real.add (x_im);
+ return z.root (2);
+ }
+ else
+ {
+ var z = copy ();
+ if (z.re_sign < 0)
+ z.re_sign = -z.re_sign;
+ return z;
+ }
+ }
+
+ /* Sets z = Arg (x) */
+ public Number arg (AngleUnit unit = AngleUnit.RADIANS)
+ {
+ if (is_zero ())
+ {
+ /* Translators: Error display when attempting to take argument of zero */
+ mperr (_("Argument not defined for zero"));
+ return new Number.integer (0);
+ }
+
+ var x_real = real_component ();
+ var x_im = imaginary_component ();
+ var pi = new Number.pi ();
+
+ Number z;
+ if (x_im.is_zero ())
+ {
+ if (x_real.is_negative ())
+ z = pi;
+ else
+ return new Number.integer (0);
+ }
+ else if (x_real.is_zero ())
+ {
+ if (x_im.is_negative ())
+ z = pi.divide_integer (-2);
+ else
+ z = pi.divide_integer (2);
+ }
+ else if (x_real.is_negative ())
+ {
+ z = x_im.divide (x_real);
+ z = z.atan (AngleUnit.RADIANS);
+ if (x_im.is_negative ())
+ z = z.subtract (pi);
+ else
+ z = z.add (pi);
+ }
+ else
+ {
+ z = x_im.divide (x_real);
+ z = z.atan (AngleUnit.RADIANS);
+ }
+
+ return z.from_radians (unit);
+ }
+
+ /* Sets z = âÌx */
+ public Number conjugate ()
+ {
+ var z = copy ();
+ z.im_sign = -z.im_sign;
+ return z;
+ }
+
+ /* Sets z = Re (x) */
+ public Number real_component ()
+ {
+ var z = copy ();
+
+ /* Clear imaginary component */
+ z.im_sign = 0;
+ z.im_exponent = 0;
+ for (var i = 0; i < z.im_fraction.length; i++)
+ z.im_fraction[i] = 0;
+
+ return z;
+ }
+
+ /* Sets z = Im (x) */
+ public Number imaginary_component ()
+ {
+ /* Copy imaginary component to real component */
+ var z = new Number ();
+ z.re_sign = im_sign;
+ z.re_exponent = im_exponent;
+
+ for (var i = 0; i < z.im_fraction.length; i++)
+ z.re_fraction[i] = im_fraction[i];
+
+ /* Clear (old) imaginary component */
+ z.im_sign = 0;
+ z.im_exponent = 0;
+ for (var i = 0; i < z.im_fraction.length; i++)
+ z.im_fraction[i] = 0;
+
+ return z;
+ }
+
+ public Number integer_component ()
+ {
+ /* Clear re_fraction */
+ var z = copy ();
+ for (var i = z.re_exponent; i < SIZE; i++)
+ z.re_fraction[i] = 0;
+ z.im_sign = 0;
+ z.im_exponent = 0;
+ for (var i = 0; i < z.im_fraction.length; i++)
+ z.im_fraction[i] = 0;
+
+ return z;
+ }
+
+ /* Sets z = x mod 1 */
+ public Number fractional_component ()
+ {
+ /* fractional component of zero is 0 */
+ if (is_zero ())
+ return new Number.integer (0);
+
+ /* All fractional */
+ if (re_exponent <= 0)
+ return this;
+
+ /* Shift fractional component */
+ var shift = re_exponent;
+ for (var i = shift; i < SIZE && re_fraction[i] == 0; i++)
+ shift++;
+ var z = new Number.integer (0);
+ z.re_sign = re_sign;
+ z.re_exponent = re_exponent - shift;
+ for (var i = 0; i < SIZE; i++)
+ {
+ if (i + shift >= SIZE)
+ z.re_fraction[i] = 0;
+ else
+ z.re_fraction[i] = re_fraction[i + shift];
+ }
+ if (z.re_fraction[0] == 0)
+ z.re_sign = 0;
+
+ return z;
+ }
+
+ /* Sets z = {x} */
+ public Number fractional_part ()
+ {
+ return subtract (floor ());
+ }
+
+ /* Sets z = âxâ */
+ public Number floor ()
+ {
+ /* Integer component of zero = 0 */
+ if (is_zero ())
+ return this;
+
+ /* If all fractional then no integer component */
+ if (re_exponent <= 0)
+ {
+ if (is_negative ())
+ return new Number.integer (-1);
+ else
+ return new Number.integer (0);
+ }
+
+ /* Clear fractional component */
+ var z = copy ();
+ var have_fraction = false;
+ for (var i = z.re_exponent; i < SIZE; i++)
+ {
+ if (z.re_fraction[i] != 0)
+ have_fraction = true;
+ z.re_fraction[i] = 0;
+ }
+ z.im_sign = 0;
+ z.im_exponent = 0;
+ for (var i = 0; i < z.im_fraction.length; i++)
+ z.im_fraction[i] = 0;
+
+ if (have_fraction && is_negative ())
+ z = z.add (new Number.integer (-1));
+
+ return z;
+ }
+
+ /* Sets z = âxâ */
+ public Number ceiling ()
+ {
+ var z = floor ();
+ var f = fractional_component ();
+ if (f.is_zero ())
+ return z;
+ return z.add (new Number.integer (1));
+ }
+
+ /* Sets z = [x] */
+ public Number round ()
+ {
+ var do_floor = !is_negative ();
+
+ var f = fractional_component ();
+ f = f.multiply_integer (2);
+ f = f.abs ();
+ if (f.compare (new Number.integer (1)) >= 0)
+ do_floor = !do_floor;
+
+ if (do_floor)
+ return floor ();
+ else
+ return ceiling ();
+ }
+
+ /* Sets z = 1 Ã x */
+ public Number reciprocal ()
+ {
+ if (is_complex ())
+ {
+ var real_x = real_component ();
+ var im_x = imaginary_component ();
+
+ /* 1/(a+bi) = (a-bi)/(a+bi)(a-bi) = (a-bi)/(aÂ+bÂ) */
+ var t1 = real_x.multiply (real_x);
+ var t2 = im_x.multiply (im_x);
+ t1 = t1.add (t2);
+ var z = t1.reciprocal_real ();
+ return conjugate ().multiply (z);
+ }
+ else
+ return reciprocal_real ();
+ }
+
+ /* Sets z = e^x */
+ public Number epowy ()
+ {
+ /* e^0 = 1 */
+ if (is_zero ())
+ return new Number.integer (1);
+
+ if (is_complex ())
+ {
+ var x_real = real_component ();
+ var theta = imaginary_component ();
+
+ var r = x_real.epowy_real ();
+ return new Number.polar (r, theta);
+ }
+ else
+ return epowy_real ();
+ }
+
+ /* Sets z = x^y */
+ public Number xpowy (Number y)
+ {
+ if (y.is_integer ())
+ return xpowy_integer (y.to_integer ());
+ else
+ {
+ var reciprocal = y.reciprocal ();
+ if (reciprocal.is_integer ())
+ return root (reciprocal.to_integer ());
+ else
+ return pwr (y);
+ }
+ }
+
+ /* Sets z = x^y */
+ public Number xpowy_integer (int64 n)
+ {
+ /* 0^-n invalid */
+ if (is_zero () && n < 0)
+ {
+ /* Translators: Error displayed when attempted to raise 0 to a negative re_exponent */
+ mperr (_("The power of zero is undefined for a negative exponent"));
+ return new Number.integer (0);
+ }
+
+ /* x^0 = 1 */
+ if (n == 0)
+ return new Number.integer (1);
+
+ /* 0^n = 0 */
+ if (is_zero ())
+ return new Number.integer (0);
+
+ /* x^1 = x */
+ if (n == 1)
+ return this;
+
+ Number t;
+ if (n < 0)
+ {
+ t = reciprocal ();
+ n = -n;
+ }
+ else
+ t = this;
+
+ /* Multply x n times */
+ // FIXME: Can do z = z.multiply (z) until close to answer (each call doubles number of multiples) */
+ var z = new Number.integer (1);
+ for (var i = 0; i < n; i++)
+ z = z.multiply (t);
+ return z;
+ }
+
+ /* Sets z = nâx */
+ public Number root (int64 n)
+ {
+ if (!is_complex () && is_negative () && n % 2 == 1)
+ {
+ var z = abs ();
+ z = z.root_real (n);
+ z = z.invert_sign ();
+ return z;
+ }
+ else if (is_complex () || is_negative ())
+ {
+ var r = abs ();
+ var theta = arg ();
+
+ r = r.root_real (n);
+ theta = theta.divide_integer (n);
+ return new Number.polar (r, theta);
+ }
+ else
+ return root_real (n);
+ }
+
+ /* Sets z = âx */
+ public Number sqrt ()
+ {
+ if (is_zero ())
+ return this;
+
+ /* FIXME: Make complex numbers optional */
+ /*if (re_sign < 0)
+ {
+ mperr (_("Square root is undefined for negative values"));
+ return new Number.integer (0);
+ }*/
+ else
+ {
+ var t = root (-2);
+ var z = multiply (t);
+ return z.ext (t.re_fraction[0], z.re_fraction[0]);
+ }
+ }
+
+ /* Sets z = ln x */
+ public Number ln ()
+ {
+ /* ln (0) undefined */
+ if (is_zero ())
+ {
+ /* Translators: Error displayed when attempting to take logarithm of zero */
+ mperr (_("Logarithm of zero is undefined"));
+ return new Number.integer (0);
+ }
+
+ /* ln (-x) complex */
+ /* FIXME: Make complex numbers optional */
+ /*if (is_negative ())
+ {
+ // Translators: Error displayed attempted to take logarithm of negative value
+ mperr (_("Logarithm of negative values is undefined"));
+ return new Number.integer (0);
+ }*/
+
+ if (is_complex () || is_negative ())
+ {
+ /* ln (re^iÎ) = e^(ln (r)+iÎ) */
+ var r = abs ();
+ var theta = arg ();
+ var z_real = r.ln_real ();
+
+ return new Number.complex (z_real, theta);
+ }
+ else
+ return ln_real ();
+ }
+
+ /* Sets z = log_n x */
+ public Number logarithm (int64 n)
+ {
+ /* log (0) undefined */
+ if (is_zero ())
+ {
+ /* Translators: Error displayed when attempting to take logarithm of zero */
+ mperr (_("Logarithm of zero is undefined"));
+ return new Number.integer (0);
+ }
+
+ /* logn (x) = ln (x) / ln (n) */
+ var t1 = new Number.integer (n);
+ return ln ().divide (t1.ln ());
+ }
+
+ /* Sets z = x! */
+ public Number factorial ()
+ {
+ /* 0! == 1 */
+ if (is_zero ())
+ return new Number.integer (1);
+ if (!is_natural ())
+ {
+ /* Translators: Error displayed when attempted take the factorial of a fractional number */
+ mperr (_("Factorial is only defined for natural numbers"));
+ return new Number.integer (0);
+ }
+
+ /* Convert to integer - if couldn't be converted then the factorial would be too big anyway */
+ var value = to_integer ();
+ var z = this;
+ for (var i = 2; i < value; i++)
+ z = z.multiply_integer (i);
+
+ return z;
+ }
+
+ /* Sets z = x + y */
+ public Number add (Number y)
+ {
+ return add_with_sign (1, y);
+ }
+
+ /* Sets z = x â y */
+ public Number subtract (Number y)
+ {
+ return add_with_sign (-1, y);
+ }
+
+ /* Sets z = x à y */
+ public Number multiply (Number y)
+ {
+ /* x*0 = 0*y = 0 */
+ if (is_zero () || y.is_zero ())
+ return new Number.integer (0);
+
+ /* (a+bi)(c+di) = (ac-bd)+(ad+bc)i */
+ if (is_complex () || y.is_complex ())
+ {
+ Number t1, t2, real_z, im_z;
+
+ var real_x = real_component ();
+ var im_x = imaginary_component ();
+ var real_y = y.real_component ();
+ var im_y = y.imaginary_component ();
+
+ t1 = real_x.multiply_real (real_y);
+ t2 = im_x.multiply_real (im_y);
+ real_z = t1.subtract (t2);
+
+ t1 = real_x.multiply_real (im_y);
+ t2 = im_x.multiply_real (real_y);
+ im_z = t1.add (t2);
+
+ return new Number.complex (real_z, im_z);
+ }
+ else
+ return multiply_real (y);
+ }
+
+ /* Sets z = x à y */
+ public Number multiply_integer (int64 y)
+ {
+ if (is_complex ())
+ {
+ var re_z = real_component ().multiply_integer_real (y);;
+ var im_z = imaginary_component ().multiply_integer_real (y);
+ return new Number.complex (re_z, im_z);
+ }
+ else
+ return multiply_integer_real (y);
+ }
+
+ /* Sets z = x à y */
+ public Number divide (Number y)
+ {
+ /* x/0 */
+ if (y.is_zero ())
+ {
+ /* Translators: Error displayed attempted to divide by zero */
+ mperr (_("Division by zero is undefined"));
+ return new Number.integer (0);
+ }
+
+ /* 0/y = 0 */
+ if (is_zero ())
+ return this;
+
+ /* z = x à yâ */
+ /* FIXME: Set re_exponent to zero to avoid overflow in multiply??? */
+ var t = y.reciprocal ();
+ var ie = t.re_exponent;
+ t.re_exponent = 0;
+ var i = t.re_fraction[0];
+ var z = multiply (t);
+ z = z.ext (i, z.re_fraction[0]);
+ z.re_exponent += ie;
+
+ return z;
+ }
+
+ /* Sets z = x à y */
+ public Number divide_integer (int64 y)
+ {
+ if (is_complex ())
+ {
+ var re_z = real_component ().divide_integer_real (y);
+ var im_z = imaginary_component ().divide_integer_real (y);
+ return new Number.complex (re_z, im_z);
+ }
+ else
+ return divide_integer_real (y);
+ }
+
+ /* Sets z = x mod y */
+ public Number modulus_divide (Number y)
+ {
+ if (!is_integer () || !y.is_integer ())
+ {
+ /* Translators: Error displayed when attemping to do a modulus division on non-integer numbers */
+ mperr (_("Modulus division is only defined for integers"));
+ return new Number.integer (0);
+ }
+
+ var t1 = divide (y).floor ();
+ var t2 = t1.multiply (y);
+ var z = subtract (t2);
+
+ t1 = new Number.integer (0);
+ if ((y.compare (t1) < 0 && z.compare (t1) > 0) || z.compare (t1) < 0)
+ z = z.add (y);
+
+ return z;
+ }
+
+ /* Sets z = sin x */
+ public Number sin (AngleUnit unit = AngleUnit.RADIANS)
+ {
+ if (is_complex ())
+ {
+ var x_real = real_component ();
+ var x_im = imaginary_component ();
+
+ var z_real = x_real.sin_real (unit);
+ var t = x_im.cosh ();
+ z_real = z_real.multiply (t);
+
+ var z_im = x_real.cos_real (unit);
+ t = x_im.sinh ();
+ z_im = z_im.multiply (t);
+
+ return new Number.complex (z_real, z_im);
+ }
+ else
+ return sin_real (unit);
+ }
+
+ /* Sets z = cos x */
+ public Number cos (AngleUnit unit = AngleUnit.RADIANS)
+ {
+ if (is_complex ())
+ {
+ var x_real = real_component ();
+ var x_im = imaginary_component ();
+
+ var z_real = x_real.cos_real (unit);
+ var t = x_im.cosh ();
+ z_real = z_real.multiply (t);
+
+ var z_im = x_real.sin_real (unit);
+ t = x_im.sinh ();
+ z_im = z_im.multiply (t);
+ z_im = z_im.invert_sign ();
+
+ return new Number.complex (z_real, z_im);
+ }
+ else
+ return cos_real (unit);
+ }
+
+ /* Sets z = tan x */
+ public Number tan (AngleUnit unit = AngleUnit.RADIANS)
+ {
+ /* Check for undefined values */
+ var cos_x = cos (unit);
+ if (cos_x.is_zero ())
+ {
+ /* Translators: Error displayed when tangent value is undefined */
+ mperr (_("Tangent is undefined for angles that are multiples of Ï (180Â) from Ïâ2 (90Â)"));
+ return new Number.integer (0);
+ }
+
+ /* tan (x) = sin (x) / cos (x) */
+ return sin (unit).divide (cos_x);
+ }
+
+ /* Sets z = sinâ x */
+ public Number asin (AngleUnit unit = AngleUnit.RADIANS)
+ {
+ /* asinâÂ(0) = 0 */
+ if (is_zero ())
+ return new Number.integer (0);
+
+ /* sinâÂ(x) = tanâÂ(x / â(1 - xÂ)), |x| < 1 */
+ if (re_exponent <= 0)
+ {
+ var t1 = new Number.integer (1);
+ var t2 = t1;
+ t1 = t1.subtract (this);
+ t2 = t2.add (this);
+ t2 = t1.multiply (t2);
+ t2 = t2.root (-2);
+ var z = multiply (t2);
+ z = z.atan (unit);
+ return z;
+ }
+
+ /* sinâÂ(1) = Ï/2, sinâÂ(-1) = -Ï/2 */
+ var t2 = new Number.integer (re_sign);
+ if (equals (t2))
+ {
+ var z = new Number.pi ().divide_integer (2 * t2.re_sign);
+ return z.from_radians (unit);
+ }
+
+ /* Translators: Error displayed when inverse sine value is undefined */
+ mperr (_("Inverse sine is undefined for values outside [-1, 1]"));
+ return new Number.integer (0);
+ }
+
+ /* Sets z = cosâ x */
+ public Number acos (AngleUnit unit = AngleUnit.RADIANS)
+ {
+ var pi = new Number.pi ();
+ var t1 = new Number.integer (1);
+ var n1 = new Number.integer (-1);
+
+ Number z;
+ if (compare (t1) > 0 || compare (n1) < 0)
+ {
+ /* Translators: Error displayed when inverse cosine value is undefined */
+ mperr (_("Inverse cosine is undefined for values outside [-1, 1]"));
+ z = new Number.integer (0);
+ }
+ else if (is_zero ())
+ z = pi.divide_integer (2);
+ else if (equals (t1))
+ z = new Number.integer (0);
+ else if (equals (n1))
+ z = pi;
+ else
+ {
+ /* cosâÂ(x) = tanâÂ(â(1 - xÂ) / x) */
+ Number y;
+ var t2 = multiply (this);
+ t2 = t1.subtract (t2);
+ t2 = t2.sqrt ();
+ t2 = t2.divide (this);
+ y = t2.atan (AngleUnit.RADIANS);
+ if (re_sign > 0)
+ z = y;
+ else
+ z = y.add (pi);
+ }
+
+ return z.from_radians (unit);
+ }
+
+ /* Sets z = tanâ x */
+ public Number atan (AngleUnit unit = AngleUnit.RADIANS)
+ {
+ if (is_zero ())
+ return new Number.integer (0);
+
+ var t2 = this;
+ var rx = 0f;
+ if (re_exponent.abs () <= 2)
+ rx = to_float ();
+
+ /* REDUCE ARGUMENT IF NECESSARY BEFORE USING SERIES */
+ var q = 1;
+ Number z;
+ while (t2.re_exponent >= 0)
+ {
+ if (t2.re_exponent == 0 && 2 * (t2.re_fraction[0] + 1) <= BASE)
+ break;
+
+ q *= 2;
+
+ /* t = t / (â(t + 1) + 1) */
+ z = t2.multiply (t2);
+ z = z.add (new Number.integer (1));
+ z = z.sqrt ();
+ z = z.add (new Number.integer (1));
+ t2 = t2.divide (z);
+ }
+
+ /* USE POWER SERIES NOW ARGUMENT IN (-0.5, 0.5) */
+ z = t2;
+ var t1 = t2.multiply (t2);
+
+ /* SERIES LOOP. REDUCE T IF POSSIBLE. */
+ for (var i = 1; ; i += 2)
+ {
+ if (T + 2 + t2.re_exponent <= 1)
+ break;
+
+ t2 = t2.multiply (t1).multiply_integer (-i).divide_integer (i + 2);
+
+ z = z.add (t2);
+ if (t2.is_zero ())
+ break;
+ }
+
+ /* CORRECT FOR ARGUMENT REDUCTION */
+ z = z.multiply_integer (q);
+
+ /* CHECK THAT RELATIVE ERROR LESS THAN 0.01 UNLESS re_exponent
+ * OF X IS LARGE (WHEN ATAN MIGHT NOT WORK)
+ */
+ if (re_exponent.abs () <= 2)
+ {
+ float ry = z.to_float ();
+ /* THE FOLLOWING MESSAGE MAY INDICATE THAT B**(T-1) IS TOO SMALL. */
+ if (Math.fabs (ry - Math.atan (rx)) >= Math.fabs (ry) * 0.01)
+ mperr ("*** ERROR OCCURRED IN ATAN, RESULT INCORRECT ***");
+ }
+
+ return z.from_radians (unit);
+ }
+
+ /* Sets z = sinh x */
+ public Number sinh ()
+ {
+ /* sinh (0) = 0 */
+ if (is_zero ())
+ return new Number.integer (0);
+
+ /* WORK WITH ABS (X) */
+ var abs_x = abs ();
+
+ /* If |x| < 1 USE EXP TO AVOID CANCELLATION, otherwise IF TOO LARGE EPOWY GIVES ERROR MESSAGE */
+ Number z;
+ if (abs_x.re_exponent <= 0)
+ {
+ /* ((e^|x| + 1) * (e^|x| - 1)) / e^|x| */
+ // FIXME: Solves to e^|x| - e^-|x|, why not lower branch always? */
+ var exp_x = abs_x.epowy ();
+ var a = exp_x.add (new Number.integer (1));
+ var b = exp_x.add (new Number.integer (-1));
+ z = a.multiply (b);
+ z = z.divide (exp_x);
+ }
+ else
+ {
+ /* e^|x| - e^-|x| */
+ var exp_x = abs_x.epowy ();
+ z = exp_x.reciprocal ();
+ z = exp_x.subtract (z);
+ }
+
+ /* DIVIDE BY TWO AND RESTORE re_sign */
+ z = z.divide_integer (2);
+ return z.multiply_integer (re_sign);
+ }
+
+ /* Sets z = cosh x */
+ public Number cosh ()
+ {
+ /* cosh (0) = 1 */
+ if (is_zero ())
+ return new Number.integer (1);
+
+ /* cosh (x) = (e^x + e^-x) / 2 */
+ var t = abs ();
+ t = t.epowy ();
+ var z = t.reciprocal ();
+ z = t.add (z);
+ return z.divide_integer (2);
+ }
+
+ /* Sets z = tanh x */
+ public Number tanh ()
+ {
+ /* tanh (0) = 0 */
+ if (is_zero ())
+ return new Number.integer (0);
+
+ var t = abs ();
+
+ /* SEE IF ABS (X) SO LARGE THAT RESULT IS +-1 */
+ var r__1 = (float) T * 0.5 * Math.log ((float) BASE);
+ var z = new Number.double (r__1);
+ if (t.compare (z) > 0)
+ return new Number.integer (re_sign);
+
+ /* If |x| >= 1/2 use ?, otherwise use ? to avoid cancellation */
+ /* |tanh (x)| = (e^|2x| - 1) / (e^|2x| + 1) */
+ t = t.multiply_integer (2);
+ if (t.re_exponent > 0)
+ {
+ t = t.epowy ();
+ z = t.add (new Number.integer (-1));
+ t = t.add (new Number.integer (1));
+ z = z.divide (t);
+ }
+ else
+ {
+ t = t.epowy ();
+ z = t.add (new Number.integer (1));
+ t = t.add (new Number.integer (-1));
+ z = t.divide (z);
+ }
+
+ /* Restore re_sign */
+ z.re_sign = re_sign * z.re_sign;
+ return z;
+ }
+
+ /* Sets z = sinhâ x */
+ public Number asinh ()
+ {
+ /* sinhâÂ(x) = ln (x + â(x + 1)) */
+ var t = multiply (this);
+ t = t.add (new Number.integer (1));
+ t = t.sqrt ();
+ t = add (t);
+ return t.ln ();
+ }
+
+ /* Sets z = coshâ x */
+ public Number acosh ()
+ {
+ /* Check x >= 1 */
+ var t = new Number.integer (1);
+ if (compare (t) < 0)
+ {
+ /* Translators: Error displayed when inverse hyperbolic cosine value is undefined */
+ mperr (_("Inverse hyperbolic cosine is undefined for values less than one"));
+ return new Number.integer (0);
+ }
+
+ /* coshâÂ(x) = ln (x + â(x - 1)) */
+ t = multiply (this);
+ t = t.add (new Number.integer (-1));
+ t = t.sqrt ();
+ t = add (t);
+ return t.ln ();
+ }
+
+ /* Sets z = tanhâ x */
+ public Number atanh ()
+ {
+ /* Check -1 <= x <= 1 */
+ if (compare (new Number.integer (1)) >= 0 || compare (new Number.integer (-1)) <= 0)
+ {
+ /* Translators: Error displayed when inverse hyperbolic tangent value is undefined */
+ mperr (_("Inverse hyperbolic tangent is undefined for values outside [-1, 1]"));
+ return new Number.integer (0);
+ }
+
+ /* atanh (x) = 0.5 * ln ((1 + x) / (1 - x)) */
+ var n = add (new Number.integer (1));
+ var d = invert_sign ();
+ d = d.add (new Number.integer (1));
+ var z = n.divide (d);
+ z = z.ln ();
+ return z.divide_integer (2);
+ }
+
+ /* Sets z = boolean AND for each bit in x and z */
+ public Number and (Number y)
+ {
+ if (!is_positive_integer () || !y.is_positive_integer ())
+ {
+ /* Translators: Error displayed when boolean AND attempted on non-integer values */
+ mperr (_("Boolean AND is only defined for positive integers"));
+ }
+
+ return bitwise (y, (v1, v2) => { return v1 & v2; }, 0);
+ }
+
+ /* Sets z = boolean OR for each bit in x and z */
+ public Number or (Number y)
+ {
+ if (!is_positive_integer () || !y.is_positive_integer ())
+ {
+ /* Translators: Error displayed when boolean OR attempted on non-integer values */
+ mperr (_("Boolean OR is only defined for positive integers"));
+ }
+
+ return bitwise (y, (v1, v2) => { return v1 | v2; }, 0);
+ }
+
+ /* Sets z = boolean XOR for each bit in x and z */
+ public Number xor (Number y)
+ {
+ if (!is_positive_integer () || !y.is_positive_integer ())
+ {
+ /* Translators: Error displayed when boolean XOR attempted on non-integer values */
+ mperr (_("Boolean XOR is only defined for positive integers"));
+ }
+
+ return bitwise (y, (v1, v2) => { return v1 ^ v2; }, 0);
+ }
+
+ /* Sets z = boolean NOT for each bit in x and z for word of length 'wordlen' */
+ public Number not (int wordlen)
+ {
+ if (!is_positive_integer ())
+ {
+ /* Translators: Error displayed when boolean XOR attempted on non-integer values */
+ mperr (_("Boolean NOT is only defined for positive integers"));
+ }
+
+ return bitwise (new Number.integer (0), (v1, v2) => { return v1 ^ 0xF; }, wordlen);
+ }
+
+ /* Sets z = x masked to 'wordlen' bits */
+ public Number mask (Number x, int wordlen)
+ {
+ /* Convert to a hexadecimal string and use last characters */
+ var text = x.to_hex_string ();
+ var len = text.length;
+ var offset = wordlen / 4;
+ offset = len > offset ? (int) len - offset: 0;
+ return mp_set_from_string (text.substring (offset), 16);
+ }
+
+ /* Sets z = x shifted by 'count' bits. Positive shift increases the value, negative decreases */
+ public Number shift (int count)
+ {
+ if (!is_integer ())
+ {
+ /* Translators: Error displayed when bit shift attempted on non-integer values */
+ mperr (_("Shift is only possible on integer values"));
+ return new Number.integer (0);
+ }
+
+ if (count >= 0)
+ {
+ var multiplier = 1;
+ for (var i = 0; i < count; i++)
+ multiplier *= 2;
+ return multiply_integer (multiplier);
+ }
+ else
+ {
+ var multiplier = 1;
+ for (var i = 0; i < -count; i++)
+ multiplier *= 2;
+ return divide_integer (multiplier).floor ();
+ }
+ }
+
+ /* Sets z to be the ones complement of x for word of length 'wordlen' */
+ public Number ones_complement (int wordlen)
+ {
+ return bitwise (new Number.integer (0), (v1, v2) => { return v1 ^ v2; }, wordlen).not (wordlen);
+ }
+
+ /* Sets z to be the twos complement of x for word of length 'wordlen' */
+ public Number twos_complement (int wordlen)
+ {
+ return ones_complement (wordlen).add (new Number.integer (1));
+ }
+
+ /* Returns a list of all prime factors in x as Numbers */
+ public List<Number?> factorize ()
+ {
+ var factors = new List<Number?> ();
+
+ var value = abs ();
+
+ if (value.is_zero ())
+ {
+ factors.append (value);
+ return factors;
+ }
+
+ if (value.equals (new Number.integer (1)))
+ {
+ factors.append (this);
+ return factors;
+ }
+
+ var divisor = new Number.integer (2);
+ while (true)
+ {
+ var tmp = value.divide (divisor);
+ if (tmp.is_integer ())
+ {
+ value = tmp;
+ factors.append (divisor);
+ }
+ else
+ break;
+ }
+
+ divisor = new Number.integer (3);
+ var root = value.sqrt ();
+ while (divisor.compare (root) <= 0)
+ {
+ var tmp = value.divide (divisor);
+ if (tmp.is_integer ())
+ {
+ value = tmp;
+ root = value.
+ sqrt ();
+ factors.append (divisor);
+ }
+ else
+ {
+ tmp = divisor.add (new Number.integer (2));
+ divisor = tmp;
+ }
+ }
+
+ if (value.compare (new Number.integer (1)) > 0)
+ factors.append (value);
+
+ if (is_negative ())
+ factors.data = factors.data.invert_sign ();
+
+ return factors;
+ }
+
+ private Number copy ()
+ {
+ var z = new Number ();
+ z.re_sign = re_sign;
+ z.im_sign = im_sign;
+ z.re_exponent = re_exponent;
+ z.im_exponent = im_exponent;
+ for (var i = 0; i < re_fraction.length; i++)
+ {
+ z.re_fraction[i] = re_fraction[i];
+ z.im_fraction[i] = im_fraction[i];
+ }
+
+ return z;
+ }
+
+ private Number add_with_sign (int y_sign, Number y)
+ {
+ if (is_complex () || y.is_complex ())
+ {
+ Number real_z, im_z;
+
+ var real_x = real_component ();
+ var im_x = imaginary_component ();
+ var real_y = y.real_component ();
+ var im_y = y.imaginary_component ();
+
+ real_z = real_x.add_real (y_sign * y.re_sign, real_y);
+ im_z = im_x.add_real (y_sign * y.im_sign, im_y);
+
+ return new Number.complex (real_z, im_z);
+ }
+ else
+ return add_real (y_sign * y.re_sign, y);
+ }
+
+ private Number epowy_real ()
+ {
+ /* e^0 = 1 */
+ if (is_zero ())
+ return new Number.integer (1);
+
+ /* If |x| < 1 use exp */
+ if (re_exponent <= 0)
+ return exp ();
+
+ /* NOW SAFE TO CONVERT X TO REAL */
+ var rx = to_double ();
+
+ /* SAVE re_sign AND WORK WITH ABS (X) */
+ var xs = re_sign;
+ var t2 = abs ();
+
+ /* GET fractional AND INTEGER PARTS OF ABS (X) */
+ var ix = t2.to_integer ();
+ t2 = t2.fractional_component ();
+
+ /* ATTACH re_sign TO fractional PART AND COMPUTE EXP OF IT */
+ t2.re_sign *= xs;
+ var z = t2.exp ();
+
+ /* COMPUTE E-2 OR 1/E USING TWO EXTRA DIGITS IN CASE ABS (X) LARGE
+ * (BUT ONLY ONE EXTRA DIGIT IF T < 4)
+ */
+ var tss = 0;
+ if (T < 4)
+ tss = T + 1;
+ else
+ tss = T + 2;
+
+ /* LOOP FOR E COMPUTATION. DECREASE T IF POSSIBLE. */
+ /* Computing MIN */
+ var t1 = new Number.integer (xs);
+
+ t2.re_sign = 0;
+ for (var i = 2 ; ; i++)
+ {
+ if (int.min (tss, tss + 2 + t1.re_exponent) <= 2)
+ break;
+
+ t1 = t1.divide_integer (i * xs);
+ t2 = t2.add (t1);
+ if (t1.is_zero ())
+ break;
+ }
+
+ /* RAISE E OR 1/E TO POWER IX */
+ if (xs > 0)
+ t2 = t2.add (new Number.integer (2));
+ t2 = t2.xpowy_integer (ix);
+
+ /* MULTIPLY EXPS OF INTEGER AND fractional PARTS */
+ z = z.multiply (t2);
+
+ /* CHECK THAT RELATIVE ERROR LESS THAN 0.01 UNLESS ABS (X) LARGE
+ * (WHEN EXP MIGHT OVERFLOW OR UNDERFLOW)
+ */
+ if (Math.fabs (rx) > 10.0f)
+ return z;
+
+ var rz = z.to_double ();
+ var r__1 = rz - Math.exp (rx);
+ if (Math.fabs (r__1) < rz * 0.01f)
+ return z;
+
+ /* THE FOLLOWING MESSAGE MAY INDICATE THAT
+ * B**(T-1) IS TOO SMALL, OR THAT M IS TOO SMALL SO THE
+ * RESULT UNDERFLOWED.
+ */
+ mperr ("*** ERROR OCCURRED IN EPOWY, RESULT INCORRECT ***");
+ return z;
+ }
+
+ /* Return e^x for |x| < 1 USING AN o (SQRt (T).m (T)) ALGORITHM
+ * DESCRIBED IN - R. P. BRENT, THE COMPLEXITY OF MULTIPLE-
+ * PRECISION ARITHMETIC (IN COMPLEXITY OF COMPUTATIONAL PROBLEM
+ * SOLVING, UNIV. OF QUEENSLAND PRESS, BRISBANE, 1976, 126-165).
+ * ASYMPTOTICALLY FASTER METHODS EXIST, BUT ARE NOT USEFUL
+ * UNLESS T IS VERY LARGE. SEE COMMENTS TO MP_ATAN AND MPPIGL.
+ */
+ private Number exp ()
+ {
+ /* e^0 = 1 */
+ if (is_zero ())
+ return new Number.integer (1);
+
+ /* Only defined for |x| < 1 */
+ if (re_exponent > 0)
+ {
+ mperr ("*** ABS (X) NOT LESS THAN 1 IN CALL TO MP_EXP ***");
+ return new Number.integer (0);
+ }
+
+ var t1 = this;
+ var rlb = Math.log (BASE);
+
+ /* Compute approximately optimal q (and divide x by 2^q) */
+ var q = (int) (Math.sqrt (T * 0.48 * rlb) + re_exponent * 1.44 * rlb);
+
+ /* HALVE Q TIMES */
+ if (q > 0)
+ {
+ var ib = BASE << 2;
+ var ic = 1;
+ for (var i = 1; i <= q; i++)
+ {
+ ic *= 2;
+ if (ic < ib && ic != BASE && i < q)
+ continue;
+ t1 = t1.divide_integer (ic);
+ ic = 1;
+ }
+ }
+
+ if (t1.is_zero ())
+ return new Number.integer (0);
+
+ /* Sum series, reducing t where possible */
+ var z = t1.copy ();
+ var t2 = t1;
+ for (var i = 2; T + t2.re_exponent - z.re_exponent > 0; i++)
+ {
+ t2 = t1.multiply (t2);
+ t2 = t2.divide_integer (i);
+ z = t2.add (z);
+ if (t2.is_zero ())
+ break;
+ }
+
+ /* Apply (x+1)^2 - 1 = x (2 + x) for q iterations */
+ for (var i = 1; i <= q; i++)
+ {
+ t1 = z.add (new Number.integer (2));
+ z = t1.multiply (z);
+ }
+
+ return z.add (new Number.integer (1));
+ }
+
+ private Number pwr (Number y)
+ {
+ /* (-x)^y imaginary */
+ /* FIXME: Make complex numbers optional */
+ /*if (re_sign < 0)
+ {
+ mperr (_("The power of negative numbers is only defined for integer exponents"));
+ return new Number.integer (0);
+ }*/
+
+ /* 0^y = 0, 0^-y undefined */
+ if (is_zero ())
+ {
+ if (y.re_sign < 0)
+ mperr (_("The power of zero is undefined for a negative exponent"));
+ return new Number.integer (0);
+ }
+
+ /* x^0 = 1 */
+ if (y.is_zero ())
+ return new Number.integer (1);
+
+ return y.multiply (ln ()).epowy ();
+ }
+
+ private Number root_real (int64 n)
+ {
+ /* x^(1/1) = x */
+ if (n == 1)
+ return this;
+
+ /* x^(1/0) invalid */
+ if (n == 0)
+ {
+ mperr (_("Root must be non-zero"));
+ return new Number.integer (0);
+ }
+
+ var np = n.abs ();
+
+ /* LOSS OF ACCURACY IF NP LARGE, SO ONLY ALLOW NP <= MAX (B, 64) */
+ if (np > int.max (BASE, 64))
+ {
+ mperr ("*** ABS (N) TOO LARGE IN CALL TO ROOT ***");
+ return new Number.integer (0);
+ }
+
+ /* 0^(1/n) = 0 for positive n */
+ if (is_zero ())
+ {
+ if (n <= 0)
+ mperr (_("Negative root of zero is undefined"));
+ return new Number.integer (0);
+ }
+
+ // FIXME: Imaginary root
+ if (re_sign < 0 && np % 2 == 0)
+ {
+ mperr (_("nth root of negative number is undefined for even n"));
+ return new Number.integer (0);
+ }
+
+ /* DIVIDE re_exponent BY NP */
+ var ex = re_exponent / np;
+
+ /* Get initial approximation */
+ var t1 = copy ();
+ t1.re_exponent = 0;
+ var approximation = Math.exp (((np * ex - re_exponent) * Math.log (BASE) - Math.log (Math.fabs (t1.to_float ()))) / np);
+ t1 = new Number.double (approximation);
+ t1.re_sign = re_sign;
+ t1.re_exponent -= (int) ex;
+
+ /* MAIN ITERATION LOOP */
+ var it0 = 3;
+ var t = it0;
+ Number t2;
+ while (true)
+ {
+ /* t1 = t1 - ((t1 * ((x * t1^np) - 1)) / np) */
+ t2 = t1.xpowy_integer (np);
+ t2 = multiply (t2);
+ t2 = t2.add (new Number.integer (-1));
+ t2 = t1.multiply (t2);
+ t2 = t2.divide_integer (np);
+ t1 = t1.subtract (t2);
+
+ /* FOLLOWING LOOP ALMOST DOUBLES T (POSSIBLE BECAUSE
+ * NEWTONS METHOD HAS 2ND ORDER CONVERGENCE).
+ */
+ if (t >= T)
+ break;
+
+ var ts3 = t;
+ var ts2 = t;
+ t = T;
+ do
+ {
+ ts2 = t;
+ t = (t + it0) / 2;
+ } while (t > ts3);
+ t = int.min (ts2, T);
+ }
+
+ /* NOW r (I2) IS X**(-1/NP)
+ * CHECK THAT NEWTON ITERATION WAS CONVERGING
+ */
+ if (t2.re_sign != 0 && (t1.re_exponent - t2.re_exponent) << 1 < T - it0)
+ {
+ /* THE FOLLOWING MESSAGE MAY INDICATE THAT B**(T-1) IS TOO SMALL,
+ * OR THAT THE INITIAL APPROXIMATION OBTAINED USING ALOG AND EXP
+ * IS NOT ACCURATE ENOUGH.
+ */
+ mperr ("*** ERROR OCCURRED IN ROOT, NEWTON ITERATION NOT CONVERGING PROPERLY ***");
+ }
+
+ if (n >= 0)
+ {
+ t1 = t1.xpowy_integer (n - 1);
+ return multiply (t1);
+ }
+
+ return t1;
+ }
+
+ /* ROUTINE CALLED BY DIVIDE AND SQRT TO ENSURE THAT
+ * RESULTS ARE REPRESENTED EXACTLY IN T-2 DIGITS IF THEY
+ * CAN BE. X IS AN MP NUMBER, I AND J ARE INTEGERS.
+ */
+ private Number ext (int i, int j)
+ {
+ if (is_zero () || T <= 2 || i == 0)
+ return this;
+
+ /* COMPUTE MAXIMUM POSSIBLE ERROR IN THE LAST PLACE */
+ var q = (j + 1) / i + 1;
+ var s = BASE * re_fraction[T - 2] + re_fraction[T - 1];
+
+ /* SET LAST TWO DIGITS TO ZERO */
+ var z = copy ();
+ if (s <= q)
+ {
+ z.re_fraction[T - 2] = 0;
+ z.re_fraction[T - 1] = 0;
+ return z;
+ }
+
+ if (s + q < BASE * BASE)
+ return z;
+
+ /* ROUND UP HERE */
+ z.re_fraction[T - 2] = BASE - 1;
+ z.re_fraction[T - 1] = BASE;
+
+ /* NORMALIZE X (LAST DIGIT B IS OK IN MULTIPLY_INTEGER) */
+ return z.multiply_integer (1);
+ }
+
+ private Number ln_real ()
+ {
+ /* LOOP TO GET APPROXIMATE Ln (X) USING SINGLE-PRECISION */
+ var t1 = copy ();
+ var z = new Number.integer (0);
+ for (var k = 0; k < 10; k++)
+ {
+ /* COMPUTE FINAL CORRECTION ACCURATELY USING LNS */
+ var t2 = t1.add (new Number.integer (-1));
+ if (t2.is_zero () || t2.re_exponent + 1 <= 0)
+ {
+ t2 = t2.lns ();
+ return z.add (t2);
+ }
+
+ /* REMOVE EXPONENT TO AVOID FLOATING-POINT OVERFLOW */
+ var e = t1.re_exponent;
+ t1.re_exponent = 0;
+ var rx = t1.to_double ();
+ t1.re_exponent = e;
+ var rlx = Math.log (rx) + e * Math.log (BASE);
+ t2 = new Number.double (-rlx);
+
+ /* UPDATE Z AND COMPUTE ACCURATE EXP OF APPROXIMATE LOG */
+ z = z.subtract (t2);
+ t2 = t2.epowy ();
+
+ /* COMPUTE RESIDUAL WHOSE LOG IS STILL TO BE FOUND */
+ t1 = t1.multiply (t2);
+ }
+
+ mperr ("*** ERROR IN LN, ITERATION NOT CONVERGING ***");
+ return z;
+ }
+
+ /* RETURNS MP Y = Ln (1+X) IF X IS AN MP NUMBER SATISFYING THE
+ * CONDITION ABS (X) < 1/B, ERROR OTHERWISE.
+ * USES NEWTONS METHOD TO SOLVE THE EQUATION
+ * EXP1(-Y) = X, THEN REVERSES re_sign OF Y.
+ */
+ private Number lns ()
+ {
+ /* ln (1+0) = 0 */
+ if (is_zero ())
+ return this;
+
+ /* Get starting approximation -ln (1+x) ~= -x + x^2/2 - x^3/3 + x^4/4 */
+ var t2 = copy ();
+ var t1 = divide_integer (4);
+ t1 = t1.add (new Number.fraction (-1, 3));
+ t1 = multiply (t1);
+ t1 = t1.add (new Number.fraction (1, 2));
+ t1 = multiply (t1);
+ t1 = t1.add (new Number.integer (-1));
+ var z = multiply (t1);
+
+ /* Solve using Newtons method */
+ var it0 = 5;
+ var t = it0;
+ Number t3;
+ while (true)
+ {
+ /* t3 = (e^t3 - 1) */
+ /* z = z - (t2 + t3 + (t2 * t3)) */
+ t3 = z.epowy ();
+ t3 = t3.add (new Number.integer (-1));
+ t1 = t2.multiply (t3);
+ t3 = t3.add (t1);
+ t3 = t2.add (t3);
+ z = z.subtract (t3);
+ if (t >= T)
+ break;
+
+ /* FOLLOWING LOOP COMPUTES NEXT VALUE OF T TO USE.
+ * BECAUSE NEWTONS METHOD HAS 2ND ORDER CONVERGENCE,
+ * WE CAN ALMOST DOUBLE T EACH TIME.
+ */
+ var ts3 = t;
+ var ts2 = t;
+ t = T;
+ do
+ {
+ ts2 = t;
+ t = (t + it0) / 2;
+ } while (t > ts3);
+ t = ts2;
+ }
+
+ /* CHECK THAT NEWTON ITERATION WAS CONVERGING AS EXPECTED */
+ if (t3.re_sign != 0 && t3.re_exponent << 1 > it0 - T)
+ mperr ("*** ERROR OCCURRED IN LNS, NEWTON ITERATION NOT CONVERGING PROPERLY ***");
+
+ z.re_sign = -z.re_sign;
+
+ return z;
+ }
+
+ private Number add_real (int y_sign, Number y)
+ {
+ var re_sign_prod = y_sign * re_sign;
+
+ /* 0 + y = y */
+ if (is_zero ())
+ {
+ if (y_sign != y.re_sign)
+ return y.invert_sign ();
+ else
+ return y;
+ }
+ /* x + 0 = x */
+ else if (y.is_zero ())
+ return this;
+
+ var exp_diff = re_exponent - y.re_exponent;
+ var med = exp_diff.abs ();
+ var x_largest = false;
+ if (exp_diff < 0)
+ x_largest = false;
+ else if (exp_diff > 0)
+ x_largest = true;
+ else
+ {
+ /* EXPONENTS EQUAL SO COMPARE SIGNS, THEN FRACTIONS IF NEC. */
+ if (re_sign_prod < 0)
+ {
+ /* signs are not equal. find out which mantissa is larger. */
+ int j;
+ for (j = 0; j < T; j++)
+ {
+ int i = re_fraction[j] - y.re_fraction[j];
+ if (i != 0)
+ {
+ if (i < 0)
+ x_largest = false;
+ else if (i > 0)
+ x_largest = true;
+ break;
+ }
+ }
+
+ /* Both mantissas equal, so result is zero. */
+ if (j >= T)
+ return new Number.integer (0);
+ }
+ }
+
+ var z = new Number.integer (0);
+
+ int[] big_fraction, small_fraction;
+ if (x_largest)
+ {
+ z.re_sign = re_sign;
+ z.re_exponent = re_exponent;
+ big_fraction = re_fraction;
+ small_fraction = y.re_fraction;
+ }
+ else
+ {
+ z.re_sign = y_sign;
+ z.re_exponent = y.re_exponent;
+ big_fraction = y.re_fraction;
+ small_fraction = re_fraction;
+ }
+
+ /* CLEAR GUARD DIGITS TO RIGHT OF X DIGITS */
+ for (var i = 3; i >= med; i--)
+ z.re_fraction[T + i] = 0;
+
+ if (re_sign_prod >= 0)
+ {
+ /* This is probably insufficient overflow detection, but it makes us not crash at least. */
+ if (T + 3 < med)
+ {
+ mperr (_("Overflow: the result couldn't be calculated"));
+ return new Number.integer (0);
+ }
+
+ /* HERE DO ADDITION, re_exponent (Y) >= re_exponent (X) */
+ var i = 0;
+ for (i = T + 3; i >= T; i--)
+ z.re_fraction[i] = small_fraction[i - med];
+
+ var c = 0;
+ for (; i >= med; i--)
+ {
+ c = big_fraction[i] + small_fraction[i - med] + c;
+
+ if (c < BASE)
+ {
+ /* NO CARRY GENERATED HERE */
+ z.re_fraction[i] = c;
+ c = 0;
+ }
+ else
+ {
+ /* CARRY GENERATED HERE */
+ z.re_fraction[i] = c - BASE;
+ c = 1;
+ }
+ }
+
+ for (; i >= 0; i--)
+ {
+ c = big_fraction[i] + c;
+ if (c < BASE)
+ {
+ z.re_fraction[i] = c;
+ i--;
+
+ /* NO CARRY POSSIBLE HERE */
+ for (; i >= 0; i--)
+ z.re_fraction[i] = big_fraction[i];
+
+ c = 0;
+ break;
+ }
+
+ z.re_fraction[i] = 0;
+ c = 1;
+ }
+
+ /* MUST SHIFT RIGHT HERE AS CARRY OFF END */
+ if (c != 0)
+ {
+ for (var j = T + 3; j > 0; j--)
+ z.re_fraction[j] = z.re_fraction[j - 1];
+ z.re_fraction[0] = 1;
+ z.re_exponent++;
+ }
+ }
+ else
+ {
+ var c = 0;
+ var i = 0;
+ for (i = T + med - 1; i >= T; i--)
+ {
+ /* HERE DO SUBTRACTION, ABS (Y) > ABS (X) */
+ z.re_fraction[i] = c - small_fraction[i - med];
+ c = 0;
+
+ /* BORROW GENERATED HERE */
+ if (z.re_fraction[i] < 0)
+ {
+ c = -1;
+ z.re_fraction[i] += BASE;
+ }
+ }
+
+ for (; i >= med; i--)
+ {
+ c = big_fraction[i] + c - small_fraction[i - med];
+ if (c >= 0)
+ {
+ /* NO BORROW GENERATED HERE */
+ z.re_fraction[i] = c;
+ c = 0;
+ }
+ else
+ {
+ /* BORROW GENERATED HERE */
+ z.re_fraction[i] = c + BASE;
+ c = -1;
+ }
+ }
+
+ for (; i >= 0; i--)
+ {
+ c = big_fraction[i] + c;
+
+ if (c >= 0)
+ {
+ z.re_fraction[i] = c;
+ i--;
+
+ /* NO CARRY POSSIBLE HERE */
+ for (; i >= 0; i--)
+ z.re_fraction[i] = big_fraction[i];
+
+ break;
+ }
+
+ z.re_fraction[i] = c + BASE;
+ c = -1;
+ }
+ }
+
+ mp_normalize (ref z);
+
+ return z;
+ }
+
+ private Number multiply_real (Number y)
+ {
+ /* x*0 = 0*y = 0 */
+ if (re_sign == 0 || y.re_sign == 0)
+ return new Number.integer (0);
+
+ var z = new Number.integer (0);
+ z.re_sign = re_sign * y.re_sign;
+ z.re_exponent = re_exponent + y.re_exponent;
+
+ var r = new Number.integer (0);
+
+ /* PERFORM MULTIPLICATION */
+ var c = 8;
+ for (var i = 0; i < T; i++)
+ {
+ var xi = re_fraction[i];
+
+ /* FOR SPEED, PUT THE NUMBER WITH MANY ZEROS FIRST */
+ if (xi == 0)
+ continue;
+
+ /* Computing MIN */
+ for (var j = 0; j < int.min (T, T + 3 - i); j++)
+ r.re_fraction[i+j+1] += xi * y.re_fraction[j];
+ c--;
+ if (c > 0)
+ continue;
+
+ /* CHECK FOR LEGAL BASE B DIGIT */
+ if (xi < 0 || xi >= BASE)
+ {
+ mperr ("*** ILLEGAL BASE B DIGIT IN CALL TO MULTIPLY, POSSIBLE OVERWRITING PROBLEM ***");
+ return new Number.integer (0);
+ }
+
+ /* PROPAGATE CARRIES AT END AND EVERY EIGHTH TIME,
+ * FASTER THAN DOING IT EVERY TIME.
+ */
+ for (var j = T + 3; j >= 0; j--)
+ {
+ int ri = r.re_fraction[j] + c;
+ if (ri < 0)
+ {
+ mperr ("*** INTEGER OVERFLOW IN MULTIPLY, B TOO LARGE ***");
+ return new Number.integer (0);
+ }
+ c = ri / BASE;
+ r.re_fraction[j] = ri - BASE * c;
+ }
+ if (c != 0)
+ {
+ mperr ("*** ILLEGAL BASE B DIGIT IN CALL TO MULTIPLY, POSSIBLE OVERWRITING PROBLEM ***");
+ return new Number.integer (0);
+ }
+ c = 8;
+ }
+
+ if (c != 8)
+ {
+ c = 0;
+ for (var i = T + 3; i >= 0; i--)
+ {
+ int ri = r.re_fraction[i] + c;
+ if (ri < 0)
+ {
+ mperr ("*** INTEGER OVERFLOW IN MULTIPLY, B TOO LARGE ***");
+ return new Number.integer (0);
+ }
+ c = ri / BASE;
+ r.re_fraction[i] = ri - BASE * c;
+ }
+
+ if (c != 0)
+ {
+ mperr ("*** ILLEGAL BASE B DIGIT IN CALL TO MULTIPLY, POSSIBLE OVERWRITING PROBLEM ***");
+ return new Number.integer (0);
+ }
+ }
+
+ /* Clear complex part */
+ z.im_sign = 0;
+ z.im_exponent = 0;
+ for (var i = 0; i < z.im_fraction.length; i++)
+ z.im_fraction[i] = 0;
+
+ /* NORMALIZE AND ROUND RESULT */
+ // FIXME: Use stack variable because of mp_normalize brokeness
+ for (var i = 0; i < SIZE; i++)
+ z.re_fraction[i] = r.re_fraction[i];
+ mp_normalize (ref z);
+
+ return z;
+ }
+
+ private Number multiply_integer_real (int64 y)
+ {
+ /* x*0 = 0*y = 0 */
+ if (is_zero () || y == 0)
+ return new Number.integer (0);
+
+ /* x*1 = x, x*-1 = -x */
+ // FIXME: Why is this not working? mp_ext is using this function to do a normalization
+ /*if (y == 1 || y == -1)
+ {
+ if (y < 0)
+ z = invert_sign ();
+ else
+ z = this;
+ return z;
+ }*/
+
+ /* Copy x as z may also refer to x */
+ var z = new Number.integer (0);
+
+ if (y < 0)
+ {
+ y = -y;
+ z.re_sign = -re_sign;
+ }
+ else
+ z.re_sign = re_sign;
+ z.re_exponent = re_exponent + 4;
+
+ /* FORM PRODUCT IN ACCUMULATOR */
+ int64 c = 0;
+
+ /* IF y*B NOT REPRESENTABLE AS AN INTEGER WE HAVE TO SIMULATE
+ * DOUBLE-PRECISION MULTIPLICATION.
+ */
+
+ /* Computing MAX */
+ if (y >= int.max (BASE << 3, 32767 / BASE))
+ {
+ /* HERE J IS TOO LARGE FOR SINGLE-PRECISION MULTIPLICATION */
+ var j1 = y / BASE;
+ var j2 = y - j1 * BASE;
+
+ /* FORM PRODUCT */
+ for (var i = T + 3; i >= 0; i--)
+ {
+ var c1 = c / BASE;
+ var c2 = c - BASE * c1;
+ var ix = 0;
+ if (i > 3)
+ ix = re_fraction[i - 4];
+
+ var t = j2 * ix + c2;
+ var is = t / BASE;
+ c = j1 * ix + c1 + is;
+ z.re_fraction[i] = (int) (t - BASE * is);
+ }
+ }
+ else
+ {
+ int64 ri = 0;
+ for (var i = T + 3; i >= 4; i--)
+ {
+ ri = y * re_fraction[i - 4] + c;
+ c = ri / BASE;
+ z.re_fraction[i] = (int) (ri - BASE * c);
+ }
+
+ /* CHECK FOR INTEGER OVERFLOW */
+ if (ri < 0)
+ {
+ mperr ("*** INTEGER OVERFLOW IN multiply_integer, B TOO LARGE ***");
+ return new Number.integer (0);
+ }
+
+ /* HAVE TO TREAT FIRST FOUR WORDS OF R SEPARATELY */
+ for (var i = 3; i >= 0; i--)
+ {
+ var t = c;
+ c = t / BASE;
+ z.re_fraction[i] = (int) (t - BASE * c);
+ }
+ }
+
+ /* HAVE TO SHIFT RIGHT HERE AS CARRY OFF END */
+ while (c != 0)
+ {
+ for (var i = T + 3; i >= 1; i--)
+ z.re_fraction[i] = z.re_fraction[i - 1];
+ var t = c;
+ c = t / BASE;
+ z.re_fraction[0] = (int) (t - BASE * c);
+ z.re_exponent++;
+ }
+
+ if (c < 0)
+ {
+ mperr ("*** INTEGER OVERFLOW IN multiply_integer, B TOO LARGE ***");
+ return new Number.integer (0);
+ }
+
+ z.im_sign = 0;
+ z.im_exponent = 0;
+ for (var i = 0; i < z.im_fraction.length; i++)
+ z.im_fraction[i] = 0;
+ mp_normalize (ref z);
+
+ return z;
+ }
+
+ private Number reciprocal_real ()
+ {
+ /* 1/0 invalid */
+ if (is_zero ())
+ {
+ mperr (_("Reciprocal of zero is undefined"));
+ return new Number.integer (0);
+ }
+
+ /* Start by approximating value using floating point */
+ var t1 = copy ();
+ t1.re_exponent = 0;
+ t1 = new Number.double (1.0 / t1.to_double ());
+ t1.re_exponent -= re_exponent;
+
+ var t = 3;
+ var it0 = t;
+ Number t2;
+ while (true)
+ {
+ /* t1 = t1 - (t1 * ((x * t1) - 1)) (2*t1 - t1^2*x) */
+ t2 = multiply (t1);
+ t2 = t2.add (new Number.integer (-1));
+ t2 = t1.multiply (t2);
+ t1 = t1.subtract (t2);
+ if (t >= T)
+ break;
+
+ /* FOLLOWING LOOP ALMOST DOUBLES T (POSSIBLE
+ * BECAUSE NEWTONS METHOD HAS 2ND ORDER CONVERGENCE).
+ */
+ var ts3 = t;
+ var ts2 = 0;
+ t = T;
+ do
+ {
+ ts2 = t;
+ t = (t + it0) / 2;
+ } while (t > ts3);
+ t = int.min (ts2, T);
+ }
+
+ /* RETURN IF NEWTON ITERATION WAS CONVERGING */
+ if (t2.re_sign != 0 && (t1.re_exponent - t2.re_exponent) << 1 < T - it0)
+ {
+ /* THE FOLLOWING MESSAGE MAY INDICATE THAT B**(T-1) IS TOO SMALL,
+ * OR THAT THE STARTING APPROXIMATION IS NOT ACCURATE ENOUGH.
+ */
+ mperr ("*** ERROR OCCURRED IN RECIPROCAL, NEWTON ITERATION NOT CONVERGING PROPERLY ***");
+ }
+
+ return t1;
+ }
+
+ private Number divide_integer_real (int64 y)
+ {
+ /* x/0 */
+ if (y == 0)
+ {
+ /* Translators: Error displayed attempted to divide by zero */
+ mperr (_("Division by zero is undefined"));
+ return new Number.integer (0);
+ }
+
+ /* 0/y = 0 */
+ if (is_zero ())
+ return new Number.integer (0);
+
+ /* Division by -1 or 1 just changes re_sign */
+ if (y == 1 || y == -1)
+ {
+ if (y < 0)
+ return invert_sign ();
+ else
+ return this;
+ }
+
+ var z = new Number.integer (0);
+ if (y < 0)
+ {
+ y = -y;
+ z.re_sign = -re_sign;
+ }
+ else
+ z.re_sign = re_sign;
+ z.re_exponent = re_exponent;
+
+ int64 c = 0;
+ int64 i = 0;
+
+ /* IF y*B NOT REPRESENTABLE AS AN INTEGER HAVE TO SIMULATE
+ * LONG DIVISION. ASSUME AT LEAST 16-BIT WORD.
+ */
+
+ /* Computing MAX */
+ var b2 = int.max (BASE << 3, 32767 / BASE);
+ if (y < b2)
+ {
+ /* LOOK FOR FIRST NONZERO DIGIT IN QUOTIENT */
+ int64 r1 = 0;
+ do
+ {
+ c = BASE * c;
+ if (i < T)
+ c += re_fraction[i];
+ i++;
+ r1 = c / y;
+ if (r1 < 0)
+ {
+ mperr ("*** INTEGER OVERFLOW IN DIVIDE_INTEGER, B TOO LARGE ***");
+ return new Number.integer (0);
+ }
+ } while (r1 == 0);
+
+ /* ADJUST re_exponent AND GET T+4 DIGITS IN QUOTIENT */
+ z.re_exponent += (int) (1 - i);
+ z.re_fraction[0] = (int) r1;
+ c = BASE * (c - y * r1);
+ int64 kh = 1;
+ if (i < T)
+ {
+ kh = T + 1 - i;
+ for (var k = 1; k < kh; k++)
+ {
+ c += re_fraction[i];
+ z.re_fraction[k] = (int) (c / y);
+ c = BASE * (c - y * z.re_fraction[k]);
+ i++;
+ }
+ if (c < 0)
+ {
+ mperr ("*** INTEGER OVERFLOW IN DIVIDE_INTEGER, B TOO LARGE ***");
+ return new Number.integer (0);
+ }
+ }
+
+ for (var k = kh; k < T + 4; k++)
+ {
+ z.re_fraction[k] = (int) (c / y);
+ c = BASE * (c - y * z.re_fraction[k]);
+ }
+ if (c < 0)
+ {
+ mperr ("*** INTEGER OVERFLOW IN DIVIDE_INTEGER, B TOO LARGE ***");
+ return new Number.integer (0);
+ }
+
+ mp_normalize (ref z);
+ return z;
+ }
+
+ /* HERE NEED SIMULATED DOUBLE-PRECISION DIVISION */
+ var j1 = y / BASE;
+ var j2 = y - j1 * BASE;
+
+ /* LOOK FOR FIRST NONZERO DIGIT */
+ var c2 = 0;
+ do
+ {
+ c = BASE * c + c2;
+ c2 = i < T ? re_fraction[i] : 0;
+ i++;
+ } while (c < j1 || (c == j1 && c2 < j2));
+
+ /* COMPUTE T+4 QUOTIENT DIGITS */
+ z.re_exponent += (int) (1 - i);
+ i--;
+
+ /* MAIN LOOP FOR LARGE ABS (y) CASE */
+ for (var k = 1; k <= T + 4; k++)
+ {
+ /* GET APPROXIMATE QUOTIENT FIRST */
+ var ir = c / (j1 + 1);
+
+ /* NOW REDUCE SO OVERFLOW DOES NOT OCCUR */
+ var iq = c - ir * j1;
+ if (iq >= b2)
+ {
+ /* HERE IQ*B WOULD POSSIBLY OVERFLOW SO INCREASE IR */
+ ir++;
+ iq -= j1;
+ }
+
+ iq = iq * BASE - ir * j2;
+ if (iq < 0)
+ {
+ /* HERE IQ NEGATIVE SO IR WAS TOO LARGE */
+ ir--;
+ iq += y;
+ }
+
+ if (i < T)
+ iq += re_fraction[i];
+ i++;
+ var iqj = iq / y;
+
+ /* r (K) = QUOTIENT, C = REMAINDER */
+ z.re_fraction[k - 1] = (int) (iqj + ir);
+ c = iq - y * iqj;
+
+ if (c < 0)
+ {
+ /* CARRY NEGATIVE SO OVERFLOW MUST HAVE OCCURRED */
+ mperr ("*** INTEGER OVERFLOW IN DIVIDE_INTEGER, B TOO LARGE ***");
+ return new Number.integer (0);
+ }
+ }
+
+ mp_normalize (ref z);
+
+ /* CARRY NEGATIVE SO OVERFLOW MUST HAVE OCCURRED */
+ mperr ("*** INTEGER OVERFLOW IN DIVIDE_INTEGER, B TOO LARGE ***");
+ return new Number.integer (0);
+ }
+
+ private Number from_radians (AngleUnit unit)
+ {
+ switch (unit)
+ {
+ default:
+ case AngleUnit.RADIANS:
+ return this;
+
+ case AngleUnit.DEGREES:
+ return multiply_integer (180).divide (new Number.pi ());
+
+ case AngleUnit.GRADIANS:
+ return multiply_integer (200).divide (new Number.pi ());
+ }
+ }
+
+ /* Convert x to radians */
+ private Number to_radians (AngleUnit unit)
+ {
+ switch (unit)
+ {
+ default:
+ case AngleUnit.RADIANS:
+ return this;
+
+ case AngleUnit.DEGREES:
+ return multiply (new Number.pi ()).divide_integer (180);
+
+ case AngleUnit.GRADIANS:
+ return multiply (new Number.pi ()).divide_integer (200);
+ }
+ }
+
+ /* z = sin (x) -1 >= x >= 1, do_sin = 1
+ * z = cos (x) -1 >= x >= 1, do_sin = 0
+ */
+ private Number sin1 (bool do_sin)
+ {
+ /* sin (0) = 0, cos (0) = 1 */
+ if (is_zero ())
+ {
+ if (do_sin)
+ return new Number.integer (0);
+ else
+ return new Number.integer (1);
+ }
+
+ var t2 = multiply (this);
+ if (t2.compare (new Number.integer (1)) > 0)
+ mperr ("*** ABS (X) > 1 IN CALL TO SIN1 ***");
+
+ Number t1;
+ int i;
+ Number z;
+ if (do_sin)
+ {
+ t1 = this;
+ z = t1;
+ i = 2;
+ }
+ else
+ {
+ t1 = new Number.integer (1);
+ z = new Number.integer (0);
+ i = 1;
+ }
+
+ /* Taylor series */
+ /* POWER SERIES LOOP. REDUCE T IF POSSIBLE */
+ var b2 = 2 * int.max (BASE, 64);
+ do
+ {
+ if (T + t1.re_exponent <= 0)
+ break;
+
+ /* IF I*(I+1) IS NOT REPRESENTABLE AS AN INTEGER, THE FOLLOWING
+ * DIVISION BY I*(I+1) HAS TO BE SPLIT UP.
+ */
+ t1 = t2.multiply (t1);
+ if (i > b2)
+ {
+ t1 = t1.divide_integer (-i);
+ t1 = t1.divide_integer (i + 1);
+ }
+ else
+ t1 = t1.divide_integer (-i * (i + 1));
+ z = t1.add (z);
+
+ i += 2;
+ } while (t1.re_sign != 0);
+
+ if (!do_sin)
+ z = z.add (new Number.integer (1));
+
+ return z;
+ }
+
+ private Number sin_real (AngleUnit unit)
+ {
+ /* sin (0) = 0 */
+ if (is_zero ())
+ return new Number.integer (0);
+
+ var x_radians = to_radians (unit);
+
+ var xs = x_radians.re_sign;
+ x_radians = x_radians.abs ();
+
+ /* USE SIN1 IF ABS (X) <= 1 */
+ Number z;
+ if (x_radians.compare (new Number.integer (1)) <= 0)
+ z = x_radians.sin1 (true);
+ /* FIND ABS (X) MODULO 2PI */
+ else
+ {
+ z = new Number.pi ().divide_integer (4);
+ x_radians = x_radians.divide (z);
+ x_radians = x_radians.divide_integer (8);
+ x_radians = x_radians.fractional_component ();
+
+ /* SUBTRACT 1/2, SAVE re_sign AND TAKE ABS */
+ x_radians = x_radians.add (new Number.fraction (-1, 2));
+ xs = -xs * x_radians.re_sign;
+ if (xs == 0)
+ return new Number.integer (0);
+
+ x_radians.re_sign = 1;
+ x_radians = x_radians.multiply_integer (4);
+
+ /* IF NOT LESS THAN 1, SUBTRACT FROM 2 */
+ if (x_radians.re_exponent > 0)
+ x_radians = x_radians.add (new Number.integer (-2));
+
+ if (x_radians.is_zero ())
+ return new Number.integer (0);
+
+ x_radians.re_sign = 1;
+ x_radians = x_radians.multiply_integer (2);
+
+ /* NOW REDUCED TO FIRST QUADRANT, IF LESS THAN PI/4 USE
+ * POWER SERIES, ELSE COMPUTE COS OF COMPLEMENT
+ */
+ if (x_radians.re_exponent > 0)
+ {
+ x_radians = x_radians.add (new Number.integer (-2));
+ x_radians = x_radians.multiply (z);
+ z = x_radians.sin1 (false);
+ }
+ else
+ {
+ x_radians = x_radians.multiply (z);
+ z = x_radians.sin1 (true);
+ }
+ }
+
+ z.re_sign = xs;
+ return z;
+ }
+
+ private Number cos_real (AngleUnit unit)
+ {
+ /* cos (0) = 1 */
+ if (is_zero ())
+ return new Number.integer (1);
+
+ /* Use power series if |x| <= 1 */
+ var z = to_radians (unit).abs ();
+ if (z.compare (new Number.integer (1)) <= 0)
+ return z.sin1 (false);
+ else
+ /* cos (x) = sin (Ï/2 - |x|) */
+ return new Number.pi ().divide_integer (2).subtract (z).sin (AngleUnit.RADIANS);
+ }
+
+ private Number bitwise (Number y, BitwiseFunc bitwise_operator, int wordlen)
+ {
+ var text1 = to_hex_string ();
+ var text2 = y.to_hex_string ();
+ var offset1 = text1.length - 1;
+ var offset2 = text2.length - 1;
+ var offset_out = wordlen / 4 - 1;
+ if (offset_out <= 0)
+ offset_out = offset1 > offset2 ? offset1 : offset2;
+ if (offset_out > 0 && (offset_out < offset1 || offset_out < offset2))
+ {
+ mperr ("Overflow. Try a bigger word size");
+ return new Number.integer (0);
+ }
+
+ var text_out = new char[offset_out + 1];
+
+ /* Perform bitwise operator on each character from right to left */
+ for (text_out[offset_out+1] = '\0'; offset_out >= 0; offset_out--)
+ {
+ int v1 = 0, v2 = 0;
+ const char digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ if (offset1 >= 0)
+ {
+ v1 = hex_to_int (text1[offset1]);
+ offset1--;
+ }
+ if (offset2 >= 0)
+ {
+ v2 = hex_to_int (text2[offset2]);
+ offset2--;
+ }
+ text_out[offset_out] = digits[bitwise_operator (v1, v2)];
+ }
+
+ return mp_set_from_string ((string) text_out, 16);
+ }
+
+ private int hex_to_int (char digit)
+ {
+ if (digit >= '0' && digit <= '9')
+ return digit - '0';
+ if (digit >= 'A' && digit <= 'F')
+ return digit - 'A' + 10;
+ if (digit >= 'a' && digit <= 'f')
+ return digit - 'a' + 10;
+ return 0;
+ }
+
+ private string to_hex_string ()
+ {
+ var serializer = new Serializer (DisplayFormat.FIXED, 16, 0);
+ return serializer.to_string (this);
+ }
+}
+
+// FIXME: Should all be in the class
+
+// FIXME: Re-add overflow and underflow detection
+
+static string? mp_error = null;
+
+/* THIS ROUTINE IS CALLED WHEN AN ERROR CONDITION IS ENCOUNTERED, AND
+ * AFTER A MESSAGE HAS BEEN WRITTEN TO STDERR.
+ */
+public void mperr (string text)
+{
+ mp_error = text;
+}
+
+/* Returns error string or null if no error */
+// FIXME: Global variable
+public string mp_get_error ()
+{
+ return mp_error;
+}
+
+/* Clear any current error */
+public void mp_clear_error ()
+{
+ mp_error = null;
+}
+
+/* Sets z from a string representation in 'text'. */
+public Number? mp_set_from_string (string str, int default_base = 10)
+{
+ if (str.index_of_char ('Â') >= 0)
+ return set_from_sexagesimal (str);
+
+ /* Find the base */
+ const unichar base_digits[] = {'â', 'â', 'â', 'â', 'â', 'â', 'â', 'â', 'â', 'â'};
+ var index = 0;
+ unichar c;
+ while (str.get_next_char (ref index, out c));
+ var end = index;
+ var number_base = 0;
+ var base_multiplier = 1;
+ while (str.get_prev_char (ref index, out c))
+ {
+ var value = -1;
+ for (var i = 0; i < base_digits.length; i++)
+ {
+ if (c == base_digits[i])
+ {
+ value = i;
+ break;
+ }
+ }
+ if (value < 0)
+ break;
+
+ end = index;
+ number_base += value * base_multiplier;
+ base_multiplier *= 10;
+ }
+ if (base_multiplier == 1)
+ number_base = default_base;
+
+ /* Check if this has a sign */
+ var negate = false;
+ index = 0;
+ str.get_next_char (ref index, out c);
+ if (c == '+')
+ negate = false;
+ else if (c == '-' || c == 'â')
+ negate = true;
+ else
+ str.get_prev_char (ref index, out c);
+
+ /* Convert integer part */
+ var z = new Number.integer (0);
+ while (str.get_next_char (ref index, out c))
+ {
+ var i = char_val (c, number_base);
+ if (i > number_base)
+ return null;
+ if (i < 0)
+ {
+ str.get_prev_char (ref index, out c);
+ break;
+ }
+
+ z = z.multiply_integer (number_base).add (new Number.integer (i));
+ }
+
+ /* Look for fraction characters, e.g. â */
+ const unichar fractions[] = {'Â', 'â', 'â', 'Â', 'Â', 'â', 'â', 'â', 'â', 'â', 'â', 'â', 'â', 'â', 'â'};
+ const int numerators[] = { 1, 1, 2, 1, 3, 1, 2, 3, 4, 1, 5, 1, 3, 5, 7};
+ const int denominators[] = { 2, 3, 3, 4, 4, 5, 5, 5, 5, 6, 6, 8, 8, 8, 8};
+ var has_fraction = false;
+ if (str.get_next_char (ref index, out c))
+ {
+ for (var i = 0; i < fractions.length; i++)
+ {
+ if (c == fractions[i])
+ {
+ var fraction = new Number.fraction (numerators[i], denominators[i]);
+ z = z.add (fraction);
+
+ /* Must end with fraction */
+ if (!str.get_next_char (ref index, out c))
+ return z;
+ else
+ return null;
+ }
+ }
+
+ /* Check for decimal point */
+ if (c == '.')
+ has_fraction = true;
+ else
+ str.get_prev_char (ref index, out c);
+ }
+
+ /* Convert fractional part */
+ if (has_fraction)
+ {
+ var numerator = new Number.integer (0);
+ var denominator = new Number.integer (1);
+
+ while (str.get_next_char (ref index, out c))
+ {
+ var i = char_val (c, number_base);
+ if (i < 0)
+ {
+ str.get_prev_char (ref index, out c);
+ break;
+ }
+
+ denominator = denominator.multiply_integer (number_base);
+ numerator = numerator.multiply_integer (number_base);
+ numerator = numerator.add (new Number.integer (i));
+ }
+
+ numerator = numerator.divide (denominator);
+ z = z.add (numerator);
+ }
+
+ if (index != end)
+ return null;
+
+ if (negate)
+ z = z.invert_sign ();
+
+ return z;
+}
+
+private int char_val (unichar c, int number_base)
+{
+ if (!c.isxdigit ())
+ return -1;
+
+ var value = c.xdigit_value ();
+
+ if (value >= number_base)
+ return -1;
+
+ return value;
+}
+
+private Number? set_from_sexagesimal (string str)
+{
+ var degree_index = str.index_of_char ('Â');
+ if (degree_index < 0)
+ return null;
+ var degrees = mp_set_from_string (str.substring (0, degree_index));
+ if (degrees == null)
+ return null;
+ var minute_start = degree_index;
+ unichar c;
+ str.get_next_char (ref minute_start, out c);
+
+ if (str[minute_start] == '\0')
+ return degrees;
+ var minute_index = str.index_of_char ('\'', minute_start);
+ if (minute_index < 0)
+ return null;
+ var minutes = mp_set_from_string (str.substring (minute_start, minute_index - minute_start));
+ if (minutes == null)
+ return null;
+ degrees = degrees.add (minutes.divide_integer (60));
+ var second_start = minute_index;
+ str.get_next_char (ref second_start, out c);
+
+ if (str[second_start] == '\0')
+ return degrees;
+ var second_index = str.index_of_char ('"', second_start);
+ if (second_index < 0)
+ return null;
+ var seconds = mp_set_from_string (str.substring (second_start, second_index - second_start));
+ if (seconds == null)
+ return null;
+ degrees = degrees.add (seconds.divide_integer (3600));
+ str.get_next_char (ref second_index, out c);
+
+ /* Skip over second marker and expect no more characters */
+ if (str[second_index] == '\0')
+ return degrees;
+ else
+ return null;
+}
+
+/* RETURNS K = K/GCD AND L = L/GCD, WHERE GCD IS THE
+ * GREATEST COMMON DIVISOR OF K AND L.
+ * SAVE INPUT PARAMETERS IN LOCAL VARIABLES
+ */
+public void mp_gcd (ref int64 k, ref int64 l)
+{
+ var i = k.abs ();
+ var j = l.abs ();
+ if (j == 0)
+ {
+ /* IF J = 0 RETURN (1, 0) UNLESS I = 0, THEN (0, 0) */
+ k = 1;
+ l = 0;
+ if (i == 0)
+ k = 0;
+ return;
+ }
+
+ /* EUCLIDEAN ALGORITHM LOOP */
+ do
+ {
+ i %= j;
+ if (i == 0)
+ {
+ k = k / j;
+ l = l / j;
+ return;
+ }
+ j %= i;
+ } while (j != 0);
+
+ /* HERE J IS THE GCD OF K AND L */
+ k = k / i;
+ l = l / i;
+}
+
+// FIXME: Is r.re_fraction large enough? It seems to be in practise but it may be T+4 instead of T
+// FIXME: There is some sort of stack corruption/use of unitialised variables here. Some functions are
+// using stack variables as x otherwise there are corruption errors. e.g. "Cos (45) - 1/Sqrt (2) = -0"
+// (try in scientific mode)
+public void mp_normalize (ref Number x)
+{
+ int start_index;
+
+ /* Find first non-zero digit */
+ for (start_index = 0; start_index < SIZE && x.re_fraction[start_index] == 0; start_index++);
+
+ /* Mark as zero */
+ if (start_index >= SIZE)
+ {
+ x.re_sign = 0;
+ x.re_exponent = 0;
+ return;
+ }
+
+ /* Shift left so first digit is non-zero */
+ if (start_index > 0)
+ {
+ x.re_exponent -= start_index;
+ var i = 0;
+ for (; (i + start_index) < SIZE; i++)
+ x.re_fraction[i] = x.re_fraction[i + start_index];
+ for (; i < SIZE; i++)
+ x.re_fraction[i] = 0;
+ }
+}
+
+/* Returns true if x is cannot be represented in a binary word of length 'wordlen' */
+public bool mp_is_overflow (Number x, int wordlen)
+{
+ var t2 = new Number.integer (2).xpowy_integer (wordlen);
+ return t2.compare (x) > 0;
+}
diff --git a/src/serializer.vala b/src/serializer.vala
new file mode 100644
index 0000000..2ba9828
--- /dev/null
+++ b/src/serializer.vala
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2010 Robin Sonefors
+ * Copyright (C) 2008-2012 Robert Ancell.
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+public enum DisplayFormat
+{
+ AUTOMATIC,
+ FIXED,
+ SCIENTIFIC,
+ ENGINEERING
+}
+
+[CCode (cheader_filename = "langinfo.h")]
+public enum NLItem
+{
+ RADIXCHAR,
+ THOUSEP
+}
+
+[CCode (cheader_filename = "langinfo.h")]
+extern unowned string nl_langinfo (NLItem item);
+
+public class Serializer : Object
+{
+ private int leading_digits; /* Number of digits to show before radix */
+ private int trailing_digits; /* Number of digits to show after radix */
+ private DisplayFormat format; /* Number display mode. */
+ private bool show_tsep; /* Set if the thousands separator should be shown. */
+ private bool show_zeroes; /* Set if trailing zeroes should be shown. */
+
+ private int number_base; /* Numeric base */
+
+ private unichar radix; /* Locale specific radix string. */
+ private unichar tsep; /* Locale specific thousands separator. */
+ private int tsep_count; /* Number of digits between separator. */
+
+ public Serializer (DisplayFormat format, int number_base, int trailing_digits)
+ {
+ var radix_string = nl_langinfo (NLItem.RADIXCHAR);
+ if (radix_string != null && radix_string != "")
+ radix = radix_string.get_char (0);
+ else
+ radix = '.';
+ var tsep_string = nl_langinfo (NLItem.THOUSEP);
+ if (tsep_string != null && tsep_string != "")
+ tsep = tsep_string.get_char (0);
+ else
+ tsep = ' ';
+ tsep_count = 3;
+
+ this.number_base = number_base;
+ leading_digits = 12;
+ this.trailing_digits = trailing_digits;
+ show_zeroes = false;
+ show_tsep = false;
+ this.format = format;
+ }
+
+ public string to_string (Number x)
+ {
+ switch (format)
+ {
+ default:
+ case DisplayFormat.AUTOMATIC:
+ int n_digits = 0;
+ var s0 = cast_to_string (x, ref n_digits);
+ if (n_digits <= leading_digits)
+ return s0;
+ else
+ return cast_to_exponential_string (x, false, ref n_digits);
+ case DisplayFormat.FIXED:
+ int n_digits = 0;
+ return cast_to_string (x, ref n_digits);
+ case DisplayFormat.SCIENTIFIC:
+ int n_digits = 0;
+ return cast_to_exponential_string (x, false, ref n_digits);
+ case DisplayFormat.ENGINEERING:
+ int n_digits = 0;
+ return cast_to_exponential_string (x, true, ref n_digits);
+ }
+ }
+
+ public Number? from_string (string str)
+ {
+ // FIXME: Move mp_set_from_string into here
+ return mp_set_from_string (str, number_base);
+ }
+
+ public void set_base (int number_base)
+ {
+ this.number_base = number_base;
+ }
+
+ public int get_base ()
+ {
+ return number_base;
+ }
+
+ public void set_radix (unichar radix)
+ {
+ this.radix = radix;
+ }
+
+ public unichar get_radix ()
+ {
+ return radix;
+ }
+
+ public void set_thousands_separator (unichar separator)
+ {
+ tsep = separator;
+ }
+
+ public unichar get_thousands_separator ()
+ {
+ return tsep;
+ }
+
+ public int get_thousands_separator_count ()
+ {
+ return tsep_count;
+ }
+
+ public void set_show_thousands_separators (bool visible)
+ {
+ show_tsep = visible;
+ }
+
+ public bool get_show_thousands_separators ()
+ {
+ return show_tsep;
+ }
+
+ public void set_show_trailing_zeroes (bool visible)
+ {
+ show_zeroes = visible;
+ }
+
+ public bool get_show_trailing_zeroes ()
+ {
+ return show_zeroes;
+ }
+
+ public int get_leading_digits ()
+ {
+ return leading_digits;
+ }
+
+ public void set_leading_digits (int leading_digits)
+ {
+ this.leading_digits = leading_digits;
+ }
+
+ public int get_trailing_digits ()
+ {
+ return trailing_digits;
+ }
+
+ public void set_trailing_digits (int trailing_digits)
+ {
+ this.trailing_digits = trailing_digits;
+ }
+
+ public DisplayFormat get_number_format ()
+ {
+ return format;
+ }
+
+ public void set_number_format (DisplayFormat format)
+ {
+ this.format = format;
+ }
+
+ private string cast_to_string (Number x, ref int n_digits)
+ {
+ var string = new StringBuilder.sized (1024);
+
+ var x_real = x.real_component ();
+ cast_to_string_real (x_real, number_base, false, ref n_digits, string);
+ if (x.is_complex ())
+ {
+ var x_im = x.imaginary_component ();
+
+ var force_sign = true;
+ if (string.str == "0")
+ {
+ string.assign ("");
+ force_sign = false;
+ }
+
+ var s = new StringBuilder.sized (1024);
+ int n_complex_digits = 0;
+ cast_to_string_real (x_im, 10, force_sign, ref n_complex_digits, s);
+ if (n_complex_digits > n_digits)
+ n_digits = n_complex_digits;
+ if (s.str == "0" || s.str == "+0" || s.str == "â0")
+ {
+ /* Ignore */
+ }
+ else if (s.str == "1")
+ {
+ string.append ("i");
+ }
+ else if (s.str == "+1")
+ {
+ string.append ("+i");
+ }
+ else if (s.str == "â1")
+ {
+ string.append ("âi");
+ }
+ else
+ {
+ if (s.str == "+0")
+ string.append ("+");
+ else if (s.str != "0")
+ string.append (s.str);
+
+ string.append ("i");
+ }
+ }
+
+ return string.str;
+ }
+
+ private void cast_to_string_real (Number x, int number_base, bool force_sign, ref int n_digits, StringBuilder string)
+ {
+ const char digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
+
+ var number = x;
+ if (number.is_negative ())
+ number = number.abs ();
+
+ /* Add rounding factor */
+ var temp = new Number.integer (number_base);
+ temp = temp.xpowy_integer (-(trailing_digits+1));
+ temp = temp.multiply_integer (number_base);
+ temp = temp.divide_integer (2);
+ var rounded_number = number.add (temp);
+
+ /* Write out the integer component least significant digit to most */
+ temp = rounded_number.floor ();
+ var i = 0;
+ do
+ {
+ if (this.number_base == 10 && show_tsep && i == tsep_count)
+ {
+ string.prepend_unichar (tsep);
+ i = 0;
+ }
+ i++;
+
+ var t = temp.divide_integer (number_base);
+ t = t.floor ();
+ var t2 = t.multiply_integer (number_base);
+
+ var t3 = temp.subtract (t2);
+
+ var d = t3.to_integer ();
+ string.prepend_c (d < 16 ? digits[d] : '?');
+ n_digits++;
+
+ temp = t;
+ } while (!temp.is_zero ());
+
+ var last_non_zero = string.len;
+
+ string.append_unichar (radix);
+
+ /* Write out the fractional component */
+ temp = rounded_number.fractional_component ();
+ for (i = 0; i < trailing_digits; i++)
+ {
+ if (temp.is_zero ())
+ break;
+
+ temp = temp.multiply_integer (number_base);
+ var digit = temp.floor ();
+ var d = digit.to_integer ();
+
+ string.append_c (digits[d]);
+
+ if (d != 0)
+ last_non_zero = string.len;
+ temp = temp.subtract (digit);
+ }
+
+ /* Strip trailing zeroes */
+ if (!show_zeroes || trailing_digits == 0)
+ string.truncate (last_non_zero);
+
+ /* Add sign on non-zero values */
+ if (string.str != "0" || force_sign)
+ {
+ if (x.is_negative ())
+ string.prepend ("â");
+ else if (force_sign)
+ string.prepend ("+");
+ }
+
+ /* Append base suffix if not in default base */
+ if (number_base != this.number_base)
+ {
+ const string sub_digits[] = {"â", "â", "â", "â", "â", "â", "â", "â", "â", "â"};
+ int multiplier = 1;
+ int b = number_base;
+
+ while (number_base / multiplier != 0)
+ multiplier *= 10;
+ while (multiplier != 1)
+ {
+ int d;
+ multiplier /= 10;
+ d = b / multiplier;
+ string.append (sub_digits[d]);
+ b -= d * multiplier;
+ }
+ }
+ }
+
+ private int cast_to_exponential_string_real (Number x, StringBuilder string, bool eng_format, ref int n_digits)
+ {
+ if (x.is_negative ())
+ string.append ("â");
+
+ var mantissa = x.abs ();
+
+ var base_ = new Number.integer (number_base);
+ var base3 = base_.xpowy_integer (3);
+ var base10 = base_.xpowy_integer (10);
+ var t = new Number.integer (1);
+ var base10inv = t.divide (base10);
+
+ var exponent = 0;
+ if (!mantissa.is_zero ())
+ {
+ while (!eng_format && mantissa.compare (base10) >= 0)
+ {
+ exponent += 10;
+ mantissa = mantissa.multiply (base10inv);
+ }
+
+ while ((!eng_format && mantissa.compare (base_) >= 0) ||
+ (eng_format && (mantissa.compare (base3) >= 0 || exponent % 3 != 0)))
+ {
+ exponent += 1;
+ mantissa = mantissa.divide (base_);
+ }
+
+ while (!eng_format && mantissa.compare (base10inv) < 0)
+ {
+ exponent -= 10;
+ mantissa = mantissa.multiply (base10);
+ }
+
+ t = new Number.integer (1);
+ while (mantissa.compare (t) < 0 || (eng_format && exponent % 3 != 0))
+ {
+ exponent -= 1;
+ mantissa = mantissa.multiply (base_);
+ }
+ }
+
+ string.append (cast_to_string (mantissa, ref n_digits));
+
+ return exponent;
+ }
+
+ private string cast_to_exponential_string (Number x, bool eng_format, ref int n_digits)
+ {
+ var string = new StringBuilder.sized (1024);
+
+ var x_real = x.real_component ();
+ var exponent = cast_to_exponential_string_real (x_real, string, eng_format, ref n_digits);
+ append_exponent (string, exponent);
+
+ if (x.is_complex ())
+ {
+ var x_im = x.imaginary_component ();
+
+ if (string.str == "0")
+ string.assign ("");
+
+ var s = new StringBuilder.sized (1024);
+ int n_complex_digits = 0;
+ exponent = cast_to_exponential_string_real (x_im, s, eng_format, ref n_complex_digits);
+ if (n_complex_digits > n_digits)
+ n_digits = n_complex_digits;
+ if (s.str == "0" || s.str == "+0" || s.str == "â0")
+ {
+ /* Ignore */
+ }
+ else if (s.str == "1")
+ {
+ string.append ("i");
+ }
+ else if (s.str == "+1")
+ {
+ string.append ("+i");
+ }
+ else if (s.str == "â1")
+ {
+ string.append ("âi");
+ }
+ else
+ {
+ if (s.str == "+0")
+ string.append ("+");
+ else if (s.str != "0")
+ string.append (s.str);
+
+ string.append ("i");
+ }
+ append_exponent (string, exponent);
+ }
+
+ return string.str.substring (0, string.len + 1);
+ }
+
+ private void append_exponent (StringBuilder string, int exponent)
+ {
+ const unichar super_digits[] = {'â', 'Â', 'Â', 'Â', 'â', 'â', 'â', 'â', 'â', 'â'};
+
+ if (exponent == 0)
+ return;
+
+ string.append ("Ã10"); // FIXME: Use the current base
+ if (exponent < 0)
+ {
+ exponent = -exponent;
+ string.append ("â");
+ }
+
+ var super_value = "%d".printf (exponent);
+ for (var i = 0; i < super_digits.length; i++)
+ string.append_unichar (super_digits[super_value[i] - '0']);
+ }
+}
diff --git a/src/test-equation.vala b/src/test-equation.vala
new file mode 100644
index 0000000..f723e41
--- /dev/null
+++ b/src/test-equation.vala
@@ -0,0 +1,601 @@
+/*
+ * Copyright (C) 2008-2012 Robert Ancell.
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+private int number_base = 10;
+private int wordlen = 32;
+private AngleUnit angle_units = AngleUnit.DEGREES;
+private bool enable_conversions = false;
+private bool enable_variables = false;
+
+private int fail_count = 0;
+private int pass_count = 0;
+
+private string error_code_to_string (ErrorCode error)
+{
+ if (error == ErrorCode.MP)
+ return "ErrorCode.MP(\"%s\")".printf (mp_get_error ());
+
+ return mp_error_code_to_string (error);
+}
+
+private void test (string expression, string expected, ErrorCode expected_error)
+{
+ var equation = new TestEquation (expression, enable_variables, enable_conversions);
+ equation.base = number_base;
+ equation.wordlen = wordlen;
+ equation.angle_units = angle_units;
+
+ ErrorCode error;
+ var result = equation.parse (out error, null);
+
+ if (result == null)
+ {
+ if (error == expected_error)
+ {
+ /*stdout.printf ("PASS: '%s' -> error %s\n", expression, error_code_to_string (error));*/
+ pass_count++;
+ }
+ else if (expected_error == ErrorCode.NONE)
+ {
+ stdout.printf ("*FAIL: '%s' -> error %s, expected result %s\n", expression, error_code_to_string (error), expected);
+ fail_count++;
+ }
+ else
+ {
+ stdout.printf ("*FAIL: '%s' -> error %s, expected error %s\n", expression, error_code_to_string (error), error_code_to_string (expected_error));
+ fail_count++;
+ }
+ }
+ else
+ {
+ var serializer = new Serializer (DisplayFormat.FIXED, number_base, 9);
+ var result_str = serializer.to_string (result);
+
+ if (expected_error != ErrorCode.NONE)
+ {
+ stdout.printf ("*FAIL: '%s' -> %s, expected error %s\n", expression, result_str, error_code_to_string (expected_error));
+ fail_count++;
+ }
+ else if (result_str != expected)
+ {
+ stdout.printf ("*FAIL: '%s' -> '%s', expected '%s'\n", expression, result_str, expected);
+ fail_count++;
+ }
+ else
+ {
+ /*stdout.printf ("PASS: '%s' -> '%s'\n", expression, result_str);*/
+ pass_count++;
+ }
+ }
+}
+
+private class TestEquation : Equation
+{
+ private bool enable_variables;
+ private bool enable_conversions;
+
+ public TestEquation (string equation, bool enable_variables, bool enable_conversions)
+ {
+ base (equation);
+ this.enable_variables = enable_variables;
+ this.enable_conversions = enable_conversions;
+ }
+
+ public override bool variable_is_defined (string name)
+ {
+ if (!enable_variables)
+ return false;
+
+ return name == "x" || name == "y";
+ }
+
+ public override Number? get_variable (string name)
+ {
+ if (!enable_variables)
+ return null;
+
+ if (name == "x")
+ return new Number.integer (2);
+ if (name == "y")
+ return new Number.integer (3);
+
+ return null;
+ }
+
+ public override Number? convert (Number x, string x_units, string z_units)
+ {
+ if (!enable_conversions)
+ return null;
+
+ return UnitManager.get_default ().convert_by_symbol (x, x_units, z_units);
+ }
+}
+
+private void test_conversions ()
+{
+ number_base = 10;
+ wordlen = 32;
+ angle_units = AngleUnit.DEGREES;
+ enable_conversions = true;
+ enable_variables = false;
+
+ /* Angle units */
+ //test ("Ï radians in degrees", "180", 0);
+ test ("100 gradians in degrees", "90", 0);
+
+ /* Length */
+ test ("1 meter in mm", "1000", 0);
+ test ("1m in mm", "1000", 0);
+ test ("1 inch in cm", "2.54", 0);
+
+ /* Area */
+ test ("1m in mmÂ", "1000000", 0);
+
+ /* Volume */
+ test ("1m in mmÂ", "1000000000", 0);
+
+ /* Weight */
+ test ("1 kg in pounds", "2.204622622", 0);
+
+ /* Duration */
+ test ("1 minute in seconds", "60", 0);
+ test ("1s in ms", "1000", 0);
+
+ /* Temperature */
+ //test ("100ËC in ËF", "", 0);
+ //test ("0ËC in ËF", "32", 0);
+ //test ("0ËK in ËC", "â273.15", 0);
+ test ("100degC in degF", "212", 0);
+ test ("0degC in degF", "32", 0);
+ test ("0 K in degC", "â273.15", 0);
+}
+
+private void test_equations ()
+{
+ number_base = 10;
+ wordlen = 32;
+ angle_units = AngleUnit.DEGREES;
+ enable_conversions = false;
+ enable_variables = true;
+
+ number_base = 2;
+ test ("2ââ", "10", 0);
+
+ number_base = 8;
+ test ("16434824ââ", "76543210", 0);
+
+ number_base = 16;
+ test ("FF", "FF", 0);
+ test ("18364758544493064720ââ", "FEDCBA9876543210", 0);
+
+ number_base = 10;
+ test ("0â", "0", 0); test ("0â", "0", 0); test ("0", "0", 0); test ("0ââ", "0", 0);
+ test ("1â", "1", 0); test ("1â", "1", 0); test ("1", "1", 0); test ("1ââ", "1", 0);
+ test ("2â", "", ErrorCode.INVALID); test ("2â", "2", 0); test ("2", "2", 0); test ("2ââ", "2", 0);
+ test ("3â", "", ErrorCode.INVALID); test ("3â", "3", 0); test ("3", "3", 0); test ("3ââ", "3", 0);
+ test ("4â", "", ErrorCode.INVALID); test ("4â", "4", 0); test ("4", "4", 0); test ("4ââ", "4", 0);
+ test ("5â", "", ErrorCode.INVALID); test ("5â", "5", 0); test ("5", "5", 0); test ("5ââ", "5", 0);
+ test ("6â", "", ErrorCode.INVALID); test ("6â", "6", 0); test ("6", "6", 0); test ("6ââ", "6", 0);
+ test ("7â", "", ErrorCode.INVALID); test ("7â", "7", 0); test ("7", "7", 0); test ("7ââ", "7", 0);
+ test ("8â", "", ErrorCode.INVALID); test ("8â", "", ErrorCode.INVALID); test ("8", "8", 0); test ("8ââ", "8", 0);
+ test ("9â", "", ErrorCode.INVALID); test ("9â", "", ErrorCode.INVALID); test ("9", "9", 0); test ("9ââ", "9", 0);
+ test ("Aâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("Aâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("A", "", ErrorCode.UNKNOWN_VARIABLE); test ("Aââ", "10", 0);
+ test ("Bâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("Bâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("B", "", ErrorCode.UNKNOWN_VARIABLE); test ("Bââ", "11", 0);
+ test ("Câ", "", ErrorCode.UNKNOWN_VARIABLE); test ("Câ", "", ErrorCode.UNKNOWN_VARIABLE); test ("C", "", ErrorCode.UNKNOWN_VARIABLE); test ("Cââ", "12", 0);
+ test ("Dâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("Dâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("D", "", ErrorCode.UNKNOWN_VARIABLE); test ("Dââ", "13", 0);
+ test ("Eâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("Eâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("E", "", ErrorCode.UNKNOWN_VARIABLE); test ("Eââ", "14", 0);
+ test ("Fâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("Fâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("F", "", ErrorCode.UNKNOWN_VARIABLE); test ("Fââ", "15", 0);
+ test ("aâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("aâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("a", "", ErrorCode.UNKNOWN_VARIABLE); test ("aââ", "10", 0);
+ test ("bâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("bâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("b", "", ErrorCode.UNKNOWN_VARIABLE); test ("bââ", "11", 0);
+ test ("câ", "", ErrorCode.UNKNOWN_VARIABLE); test ("câ", "", ErrorCode.UNKNOWN_VARIABLE); test ("c", "", ErrorCode.UNKNOWN_VARIABLE); test ("cââ", "12", 0);
+ test ("dâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("dâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("d", "", ErrorCode.UNKNOWN_VARIABLE); test ("dââ", "13", 0);
+ test ("eâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("eâ", "", ErrorCode.UNKNOWN_VARIABLE); /* e is a built-in variable */ test ("eââ", "14", 0);
+ test ("fâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("fâ", "", ErrorCode.UNKNOWN_VARIABLE); test ("f", "", ErrorCode.UNKNOWN_VARIABLE); test ("fââ", "15", 0);
+
+ test ("+1", "1", 0);
+ test ("â1", "â1", 0);
+ test ("+ 1", "1", 0); // FIXME: Should this be allowed?
+ test ("â 1", "â1", 0); // FIXME: Should this be allowed?
+ test ("++1", "1", ErrorCode.INVALID);
+ test ("ââ1", "1", 0);
+ test ("255", "255", 0);
+ test ("256", "256", 0);
+ test ("Â", "0.5", 0);
+ test ("1Â", "1.5", 0);
+ test ("0Â", "0", 0);
+ test ("1Â", "1", 0);
+ test ("0Â30'", "0.5", 0);
+ //test ("0Â0.1'", "1", 0); // FIXME: Not yet supported
+ test ("0Â0'1\"", "0.000277778", 0);
+ test ("0Â0'0.1\"", "0.000027778", 0);
+ test ("1.00", "1", 0);
+ test ("1.01", "1.01", 0);
+
+ test ("ÙÙÙÙÙÙÙÙÙÙ", "1234567890", 0);
+ test ("ÛÛÛÛÛÛÛÛÛÛ", "1234567890", 0);
+
+/*
+ //test ("2A", "2000000000000000", 0);
+ test ("2T", "2000000000000", 0);
+ test ("2G", "2000000000", 0);
+ test ("2M", "2000000", 0);
+ test ("2k", "2000", 0);
+ test ("2c", "0.02", 0);
+ test ("2d", "0.2", 0);
+ test ("2c", "0.02", 0);
+ test ("2m", "0.002", 0);
+ test ("2u", "0.000002", 0);
+ test ("2Â", "0.000002", 0);
+ test ("2n", "0.000000002", 0);
+ //test ("2p", "0.000000000002", 0); // FIXME: Need to print out significant figures, not decimal places
+ //test ("2f", "0.000000000000002", 0); // FIXME: Need to print out significant figures, not decimal places
+ //test ("2A3", "2300000000000000", 0);
+ test ("2T3", "2300000000000", 0);
+ test ("2G3", "2300000000", 0);
+ test ("2M3", "2300000", 0);
+ test ("2k3", "2300", 0);
+ test ("2c3", "0.023", 0);
+ test ("2d3", "0.23", 0);
+ test ("2c3", "0.023", 0);
+ test ("2m3", "0.0023", 0);
+ test ("2u3", "0.0000023", 0);
+ test ("2Â3", "0.0000023", 0);
+ //test ("2n3", "0.0000000023", 0); // FIXME: Need to print out significant figures, not decimal places
+ //test ("2p3", "0.0000000000023", 0); // FIXME: Need to print out significant figures, not decimal places
+ //test ("2f3", "0.0000000000000023", 0); // FIXME: Need to print out significant figures, not decimal places
+*/
+
+ test ("2Ã10^3", "2000", 0);
+ test ("2Ã10^â3", "0.002", 0);
+
+ test ("x", "2", 0);
+ test ("y", "3", 0);
+ test ("z", "", ErrorCode.UNKNOWN_VARIABLE);
+ test ("2y", "6", 0);
+ test ("y2", "", ErrorCode.INVALID);
+ test ("y 2", "", ErrorCode.INVALID);
+ test ("2z", "", ErrorCode.UNKNOWN_VARIABLE);
+ test ("z2", "", ErrorCode.UNKNOWN_VARIABLE);
+ test ("z 2", "", ErrorCode.UNKNOWN_VARIABLE);
+ test ("z(2)", "", ErrorCode.UNKNOWN_VARIABLE);
+ test ("yÂ", "9", 0);
+ test ("2yÂ", "18", 0);
+ test ("xÃy", "6", 0);
+ test ("xy", "6", 0);
+ test ("yx", "6", 0);
+ test ("2xy", "12", 0);
+ test ("xÂy", "12", 0);
+ test ("xyÂ", "18", 0);
+ test ("(xy)Â", "36", 0);
+ test ("2xÂy", "24", 0);
+ test ("2xyÂ", "36", 0);
+ test ("2xÂyÂ", "72", 0);
+ test ("xÂyxÂy", "144", 0);
+ test ("xÂ+2xÂâ5", "11", 0);
+ test ("2(x+3y)", "22", 0);
+ test ("x(x+3y)", "22", 0);
+ test ("(x+3y)(2x-4y)", "â88", 0);
+ test ("2xÂ+2xyâ12yÂ", "â88", 0);
+
+ test ("Ï", "3.141592654", 0);
+ test ("e", "2.718281828", 0);
+
+ test ("z=99", "99", 0);
+ test ("longname=99", "99", 0);
+ //test ("e=99", "", ErrorCode.BUILTIN_VARIABLE);
+
+ test ("0+0", "0", 0);
+ test ("1+1", "2", 0);
+ test ("1+4", "5", 0);
+ test ("4+1", "5", 0);
+ test ("40000+0.001", "40000.001", 0);
+ test ("0.001+40000", "40000.001", 0);
+ test ("2-3", "â1", 0);
+ test ("2â3", "â1", 0);
+ test ("3â2", "1", 0);
+ test ("40000â0.001", "39999.999", 0);
+ test ("0.001â40000", "â39999.999", 0);
+ test ("2*3", "6", 0);
+ test ("2Ã3", "6", 0);
+ test ("â2Ã3", "â6", 0);
+ test ("2Ãâ3", "â6", 0);
+ test ("â2Ãâ3", "6", 0);
+ test ("6/3", "2", 0);
+ test ("6Ã3", "2", 0);
+ test ("1Ã2", "0.5", 0);
+ test ("â6Ã3", "â2", 0);
+ test ("6Ãâ3", "â2", 0);
+ test ("â6Ãâ3", "2", 0);
+ test ("(â3)Ã(â6)", "0.5", 0);
+ test ("2Ã2", "1", 0);
+ test ("1203Ã1", "1203", 0);
+ test ("â0Ã32352.689", "0", 0);
+ test ("1Ã4", "0.25", 0);
+ test ("1Ã3", "0.333333333", 0);
+ test ("2Ã3", "0.666666667", 0);
+ test ("1Ã0", "", ErrorCode.MP);
+ test ("0Ã0", "", ErrorCode.MP);
+
+ /* Precision */
+ test ("1000000000000000â1000000000000000", "0", 0);
+ test ("1000000000000000Ã1000000000000000", "1", 0);
+ test ("1000000000000000Ã0.000000000000001", "1", 0);
+
+ /* Order of operations */
+ test ("1â0.9â0.1", "0", 0);
+ test ("1+2Ã3", "7", 0);
+ test ("1+(2Ã3)", "7", 0);
+ test ("(1+2)Ã3", "9", 0);
+ test ("(1+2Ã3)", "7", 0);
+ test ("2(1+1)", "4", 0);
+ test ("4Ã2(1+1)", "4", 0);
+
+ /* Percentage */
+ test ("100%", "1", 0);
+ test ("1%", "0.01", 0);
+ test ("100+1%", "101", 0);
+ test ("100â1%", "99", 0);
+ test ("100Ã1%", "1", 0);
+ test ("100Ã1%", "10000", 0);
+
+ /* Factorial */
+ test ("0!", "1", 0);
+ test ("1!", "1", 0);
+ test ("5!", "120", 0);
+ test ("69!", "171122452428141311372468338881272839092270544893520369393648040923257279754140647424000000000000000", 0);
+ test ("0.1!", "", ErrorCode.MP);
+ test ("â1!", "â1", 0);
+ test ("(â1)!", "", ErrorCode.MP);
+ test ("â(1!)", "â1", 0);
+
+ /* Powers */
+ test ("2Â", "4", 0);
+ test ("2Â", "8", 0);
+ test ("2Ââ", "1024", 0);
+ test ("(1+2)Â", "9", 0);
+ test ("(x)Â", "4", 0);
+ test ("|1â3|Â", "4", 0);
+ test ("|x|Â", "4", 0);
+ test ("0^0", "1", 0);
+ test ("0^0.5", "0", 0);
+ test ("2^0", "1", 0);
+ test ("2^1", "2", 0);
+ test ("2^2", "4", 0);
+ test ("2âÂ", "0.5", 0);
+ test ("2â", "", ErrorCode.MP);
+ test ("2^â1", "0.5", 0);
+ test ("2^(â1)", "0.5", 0);
+ test ("xâÂ", "0.5", 0);
+ test ("â10^2", "â100", 0);
+ test ("(â10)^2", "100", 0);
+ test ("â(10^2)", "â100", 0);
+ test ("2^100", "1267650600228229401496703205376", 0);
+ test ("4^3^2", "262144", 0);
+ test ("4^(3^2)", "262144", 0);
+ test ("(4^3)^2", "4096", 0);
+ test ("â4", "2", 0);
+ test ("â4â2", "0", 0);
+ test ("â8", "2", 0);
+ test ("â16", "2", 0);
+ test ("ââ8", "2", 0);
+ test ("âââ1024", "2", 0);
+ test ("â(2+2)", "2", 0);
+ test ("2â4", "4", 0);
+ test ("2Ãâ4", "4", 0);
+ test ("Sqrt (4)", "2", 0);
+ test ("Sqrt (2)", "1.414213562", 0);
+ test ("4^0.5", "2", 0);
+ test ("2^0.5", "1.414213562", 0);
+ test ("âââ8", "â2", 0);
+ test ("(â8)^(1Ã3)", "â2", 0);
+
+ test ("0 mod 7", "0", 0);
+ test ("6 mod 7", "6", 0);
+ test ("7 mod 7", "0", 0);
+ test ("8 mod 7", "1", 0);
+ test ("â1 mod 7", "6", 0);
+
+ test ("sgn 0", "0", 0);
+ test ("sgn 3", "1", 0);
+ test ("sgn â3", "â1", 0);
+ test ("â3â", "3", 0);
+ test ("â3â", "3", 0);
+ test ("[3]", "3", 0);
+ test ("ââ3â", "â3", 0);
+ test ("ââ3â", "â3", 0);
+ test ("[â3]", "â3", 0);
+ test ("â3.2â", "3", 0);
+ test ("â3.2â", "4", 0);
+ test ("[3.2]", "3", 0);
+ test ("ââ3.2â", "â4", 0);
+ test ("ââ3.2â", "â3", 0);
+ test ("[â3.2]", "â3", 0);
+ test ("â3.5â", "3", 0);
+ test ("â3.5â", "4", 0);
+ test ("[3.5]", "4", 0);
+ test ("ââ3.5â", "â4", 0);
+ test ("ââ3.5â", "â3", 0);
+ test ("[â3.5]", "â4", 0);
+ test ("â3.7â", "3", 0);
+ test ("â3.7â", "4", 0);
+ test ("[3.7]", "4", 0);
+ test ("ââ3.7â", "â4", 0);
+ test ("ââ3.7â", "â3", 0);
+ test ("[â3.7]", "â4", 0);
+ test ("{3.2}", "0.2", 0);
+ test ("{â3.2}", "0.8", 0);
+
+ test ("|1|", "1", 0);
+ test ("|â1|", "1", 0);
+ test ("|3â5|", "2", 0);
+ test ("|x|", "2", 0);
+ test ("abs 1", "1", 0);
+ test ("abs (â1)", "1", 0);
+
+ test ("log 0", "", ErrorCode.MP);
+ test ("log 1", "0", 0);
+ test ("log 2", "0.301029996", 0);
+ test ("log 10", "1", 0);
+ test ("logââ 10", "1", 0);
+ test ("logâ 2", "1", 0);
+ test ("2 log 2", "0.602059991", 0);
+
+ test ("ln 0", "", ErrorCode.MP);
+ test ("ln 1", "0", 0);
+ test ("ln 2", "0.693147181", 0);
+ test ("ln e", "1", 0);
+ test ("2 ln 2", "1.386294361", 0);
+
+ angle_units = AngleUnit.DEGREES;
+ test ("sin 0", "0", 0);
+ test ("sin 45 â 1Ãâ2", "0", 0);
+ test ("sin 20 + sin(â20)", "0", 0);
+ test ("sin 90", "1", 0);
+ test ("sin 180", "0", 0);
+ test ("2 sin 90", "2", 0);
+ test ("sinÂ45", "0.5", 0);
+
+ test ("cos 0", "1", 0);
+ test ("cos 45 â 1Ãâ2", "0", 0);
+ test ("cos 20 â cos (â20)", "0", 0);
+ test ("cos 90", "0", 0);
+ test ("cos 180", "â1", 0);
+ test ("2 cos 0", "2", 0);
+ test ("cosÂ45", "0.5", 0);
+
+ test ("tan 0", "0", 0);
+ test ("tan 10 â sin 10Ãcos 10", "0", 0);
+ test ("tan 90", "", ErrorCode.MP);
+ test ("tan 10", "0.176326981", 0);
+ test ("tanÂ10", "0.031091204", 0);
+
+ test ("cosâ 0", "90", 0);
+ test ("cosâ 1", "0", 0);
+ test ("cosâ (â1)", "180", 0);
+ test ("cosâ (1Ãâ2)", "45", 0);
+ test ("acos 0", "90", 0);
+ test ("acos 1", "0", 0);
+
+ test ("sinâ 0", "0", 0);
+ test ("sinâ 1", "90", 0);
+ test ("sinâ (â1)", "â90", 0);
+ test ("sinâ (1Ãâ2)", "45", 0);
+ test ("asin 0", "0", 0);
+ test ("asin 1", "90", 0);
+
+ test ("cosh 0", "1", 0);
+ test ("cosh 10 â (e^10 + e^â10)Ã2", "0", 0);
+
+ test ("sinh 0", "0", 0);
+ test ("sinh 10 â (e^10 â e^â10)Ã2", "0", 0);
+ test ("sinh (â10) + sinh 10", "0", 0);
+
+ test ("cosh (â5) â sinh (â5)", "1", 0);
+ test ("tanh 0", "0", 0);
+ test ("tanh 10 â sinh 10 Ã cosh 10", "0", 0);
+
+ test ("atanh 0", "0", 0);
+ test ("atanh (1Ã10) â 0.5 ln(11Ã9)", "0", 0);
+
+ angle_units = AngleUnit.DEGREES;
+ test ("sin 90", "1", 0);
+
+ angle_units = AngleUnit.RADIANS;
+ test ("sin (ÏÃ2)", "1", 0); // FIXME: Shouldn't need brackets
+
+ angle_units = AngleUnit.GRADIANS;
+ test ("sin 100", "1", 0);
+
+ /* Complex numbers */
+ angle_units = AngleUnit.DEGREES;
+ test ("i", "i", 0);
+ test ("âi", "âi", 0);
+ test ("2i", "2i", 0);
+ test ("1+i", "1+i", 0);
+ test ("i+1", "1+i", 0);
+ test ("1âi", "1âi", 0);
+ test ("iâ1", "â1+i", 0);
+ test ("iÃi", "â1", 0);
+ test ("iÃi", "1", 0);
+ test ("1Ãi", "âi", 0);
+ test ("|i|", "1", 0);
+ test ("|3+4i|", "5", 0);
+ test ("arg 0", "", ErrorCode.MP);
+ test ("arg 1", "0", 0);
+ test ("arg (1+i)", "45", 0);
+ test ("arg i", "90", 0);
+ test ("arg (â1+i)", "135", 0);
+ test ("arg â1", "180", 0);
+ test ("arg (1+âi)", "â45", 0);
+ test ("arg âi", "â90", 0);
+ test ("arg (â1âi)", "â135", 0);
+ test ("iâÂ", "âi", 0);
+ test ("ââ1", "i", 0);
+ test ("(â1)^0.5", "i", 0);
+ test ("ââ4", "2i", 0);
+ test ("e^iÏ", "â1", 0);
+ test ("log (â10) â (1 + ÏiÃln(10))", "0", 0);
+ test ("ln (âe) â (1 + Ïi)", "0", 0);
+ test ("sin(iÏÃ4) â iÃsinh(ÏÃ4)", "0", 0);
+ test ("cos(iÏÃ4) â cosh(ÏÃ4)", "0", 0);
+
+ /* Boolean */
+ test ("0 and 0", "0", 0);
+ test ("1 and 0", "0", 0);
+ test ("0 and 1", "0", 0);
+ test ("1 and 1", "1", 0);
+ test ("3 and 5", "1", 0);
+
+ test ("0 or 0", "0", 0);
+ test ("1 or 0", "1", 0);
+ test ("0 or 1", "1", 0);
+ test ("1 or 1", "1", 0);
+ test ("3 or 5", "7", 0);
+
+ test ("0 xor 0", "0", 0);
+ test ("1 xor 0", "1", 0);
+ test ("0 xor 1", "1", 0);
+ test ("1 xor 1", "0", 0);
+ test ("3 xor 5", "6", 0);
+
+ number_base = 16;
+ test ("ones 1", "FFFFFFFE", 0);
+ test ("ones 7FFFFFFF", "80000000", 0);
+ test ("twos 1", "FFFFFFFF", 0);
+ test ("twos 7FFFFFFF", "80000001", 0);
+ test ("~7Aââ", "FFFFFF85", 0);
+
+ number_base = 2;
+ wordlen = 4;
+ test ("1100â1010", "1000", 0);
+ test ("1100â1010", "1110", 0);
+ test ("1100â1010", "110", 0);
+ test ("1100â1010", "110", 0);
+ //test ("1100â1010", "0111", 0);
+ //test ("1100â1010", "0001", 0);
+ //wordlen = 2;
+ //test ("Â01â", "10â", 0);
+ //test ("ÂÂ10â", "10â", 0);
+}
+
+public int main (string args[])
+{
+ Intl.setlocale (LocaleCategory.ALL, "C");
+
+ test_conversions ();
+ test_equations ();
+
+ if (fail_count == 0)
+ stdout.printf ("Passed all %i tests\n", pass_count);
+ else
+ stdout.printf ("Failed %i/%d tests\n", fail_count, pass_count + fail_count);
+
+ return fail_count;
+}
diff --git a/src/test-number.vala b/src/test-number.vala
new file mode 100644
index 0000000..5b73dd9
--- /dev/null
+++ b/src/test-number.vala
@@ -0,0 +1,1005 @@
+/*
+ * Copyright (C) 2008-2012 Robert Ancell.
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+private int fail_count = 0;
+private int pass_count = 0;
+
+private void pass (string? text = null)
+{
+ //stdout.printf ("PASS: %s\n", text);
+ pass_count++;
+}
+
+private void fail (string text)
+{
+ stdout.printf ("*FAIL: %s\n", text);
+ fail_count++;
+}
+
+private void test_integer ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ var z = new Number.integer (a);
+ if (z.to_integer () != a)
+ {
+ fail ("Number.integer (%d).to_integer () -> %lli, expected %i".printf (a, z.to_integer (), a));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_unsigned_integer ()
+{
+ for (var a = 0; a <= 10; a++)
+ {
+ var z = new Number.unsigned_integer (a);
+ if (z.to_unsigned_integer () != a)
+ {
+ fail ("Number.unsigned_integer (%d).to_unsigned_integer () -> %i, expected %i".printf (a, (int) z.to_unsigned_integer (), a));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_fraction ()
+{
+ for (var a = 0; a <= 10; a++)
+ {
+ for (var b = 1; b <= 10; b++)
+ {
+ var z = new Number.fraction (a, b);
+ var expected = (double) a / b;
+ if (!double_matches (z, expected))
+ {
+ fail ("Number.fraction (%d, %d) -> %f, expected %f".printf (a, b, z.to_double (), expected));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+private void test_float ()
+{
+ for (var a = -10.0f; a <= 10.0f; a += 0.5f)
+ {
+ var z = new Number.float (a);
+ if (z.to_float () != a)
+ {
+ fail ("Number.float (%f).to_float () -> %f, expected %f".printf (a, z.to_float (), a));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_double ()
+{
+ for (var a = -10.0; a <= 10.0; a += 0.5)
+ {
+ var z = new Number.double (a);
+ if (z.to_double () != a)
+ {
+ fail ("Number.double (%f).to_double () -> %f, expected %f".printf (a, z.to_double (), a));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_complex ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ var z = new Number.complex (new Number.integer (a), new Number.integer (b));
+ var re_expected = a;
+ var im_expected = b;
+ if (z.real_component ().to_integer () != re_expected || z.imaginary_component ().to_integer () != im_expected)
+ {
+ fail ("Number.complex (%d%+di) -> %d%+di, expected %d%+di".printf (a, b, (int) z.real_component ().to_integer (), (int) z.imaginary_component ().to_integer (), re_expected, im_expected));
+ return;
+ }
+ }
+ }
+}
+
+private void test_polar ()
+{
+ for (var ri = -10; ri <= 10; ri++)
+ {
+ for (var theta_i = -10; theta_i <= 10; theta_i++)
+ {
+ var r = (double) ri;
+ var theta = 2 * Math.PI * theta_i / 10.0;
+ var z = new Number.polar (new Number.double (r), new Number.double (theta));
+ var re_expected = r * Math.cos (theta);
+ var im_expected = r * Math.sin (theta);
+ if (!double_matches (z.real_component (), re_expected) || !double_matches (z.imaginary_component (), im_expected))
+ {
+ fail ("Number.polar (%f, %f) -> %f%+fi, expected %f%+fi".printf (r, theta, z.real_component ().to_double (), z.imaginary_component ().to_double (), re_expected, im_expected));
+ return;
+ }
+ }
+ }
+}
+
+private void test_i ()
+{
+ var z = new Number.i ();
+ if (z.real_component ().to_integer () != 0 && z.imaginary_component ().to_integer () != 1)
+ {
+ fail ("Number.i () -> %d%+di, expected i".printf ((int) z.real_component ().to_integer (), (int) z.imaginary_component ().to_integer ()));
+ return;
+ }
+
+ pass ();
+}
+
+private void test_pi ()
+{
+ var z = new Number.pi ();
+ var expected = Math.PI;
+ if (!double_matches (z, expected))
+ {
+ fail ("Number.pi () -> %f, expected %f".printf (z.to_double (), expected));
+ return;
+ }
+
+ pass ();
+}
+
+private void test_eulers ()
+{
+ var z = new Number.eulers ();
+ var expected = Math.E;
+ if (!double_matches (z, expected))
+ {
+ fail ("Number.eulers () -> %f, expected %f".printf (z.to_double (), expected));
+ return;
+ }
+
+ pass ();
+}
+
+private void test_string ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ var s = "%d".printf (a);
+ var z = mp_set_from_string (s);
+ if (z == null)
+ {
+ fail ("mp_set_from_string (\"%s\") -> null".printf (s));
+ return;
+ }
+
+ if (z.to_integer () != a)
+ {
+ fail ("mp_set_from_string (\"%s\").to_integer () -> %d, expected %d".printf (s, (int) z.to_integer (), a));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_sgn ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ var z = new Number.integer (a);
+ var expected = 0;
+ if (a < 0)
+ expected = -1;
+ if (a > 0)
+ expected = 1;
+ if (z.sgn ().to_integer () != expected)
+ {
+ fail ("(%d).sgn () -> %d, expected %d".printf (a, (int) z.sgn ().to_integer (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_invert_sign ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ var z = new Number.integer (a);
+ var expected = -a;
+ if (z.invert_sign ().to_integer () != expected)
+ {
+ fail ("(%d).invert_sign () -> %d, expected %d".printf (a, (int) z.invert_sign ().to_integer (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_abs ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ var z = new Number.integer (a);
+ var expected = a.abs ();
+ if (z.abs ().to_integer () != expected)
+ {
+ fail ("(%d).abs () -> %d, expected %d".printf (a, (int) z.abs ().to_integer (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_arg ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ var z = new Number.complex (new Number.integer (a), new Number.integer (b));
+ z = z.arg ();
+ var expected = Math.atan2 (b, a);
+ if (!double_matches (z.real_component (), expected) || !z.imaginary_component ().is_zero ())
+ {
+ fail ("(%d%+di).arg () -> %f%+fi, expected %f".printf (a, b, z.real_component ().to_double (), z.imaginary_component ().to_double (), expected));
+ return;
+ }
+ }
+ }
+}
+
+private void test_conjugate ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ var z = new Number.complex (new Number.integer (a), new Number.integer (b));
+ z = z.conjugate ();
+ var re_expected = a;
+ var im_expected = -b;
+ if (z.real_component ().to_integer () != re_expected || z.imaginary_component ().to_integer () != im_expected)
+ {
+ fail ("(%d%+di).real_component () -> %d%+di, expected %d%+di".printf (a, b, (int) z.real_component ().to_integer (), (int) z.imaginary_component ().to_integer (), re_expected, im_expected));
+ return;
+ }
+ }
+ }
+}
+
+private void test_real_component ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ var z = new Number.complex (new Number.integer (a), new Number.integer (b));
+ var expected = a;
+ if (z.real_component ().to_integer () != expected)
+ {
+ fail ("(%d+%di).real_component () -> %d, expected %d".printf (a, b, (int) z.real_component ().to_integer (), expected));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+private void test_imaginary_component ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ var z = new Number.complex (new Number.integer (a), new Number.integer (b));
+ var expected = b;
+ if (z.imaginary_component ().to_integer () != expected)
+ {
+ fail ("(%d+%di).imaginary_component () -> %d, expected %d".printf (a, b, (int) z.imaginary_component ().to_integer (), expected));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+private bool double_matches (Number a, double b)
+{
+ return double_string (a.to_double ()) == double_string (b);
+}
+
+private string double_string (double x)
+{
+ var value = "%.6f".printf (x);
+ if (value == "-0.000000")
+ return "0.000000";
+ else
+ return value;
+}
+
+private void test_integer_component ()
+{
+ for (var ai = -100; ai <= 100; ai++)
+ {
+ var a = ai / 10.0;
+ var z = new Number.double (a).integer_component ();
+ var expected = Math.trunc (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).integer_component () -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_fractional_component ()
+{
+ for (var ai = -100; ai <= 100; ai++)
+ {
+ var a = ai / 10.0;
+ var z = new Number.double (a).fractional_component ();
+ var expected = a - Math.trunc (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).fractional_component () -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_fractional_part ()
+{
+ for (var ai = -100; ai <= 100; ai++)
+ {
+ var a = ai / 10.0;
+ var z = new Number.double (a).fractional_part ();
+ var expected = a - Math.floor (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).fractional_part () -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_floor ()
+{
+ for (var ai = -100; ai <= 100; ai++)
+ {
+ var a = ai / 10.0;
+ var z = new Number.double (a).floor ();
+ var expected = Math.floor (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).floor () -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_ceiling ()
+{
+ for (var ai = -100; ai <= 100; ai++)
+ {
+ var a = ai / 10.0;
+ var z = new Number.double (a).ceiling ();
+ var expected = Math.ceil (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).ceiling () -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_round ()
+{
+ for (var ai = -100; ai <= 100; ai++)
+ {
+ var a = ai / 10.0;
+ var z = new Number.double (a).round ();
+ var expected = Math.round (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).round () -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_reciprocal ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ if (a == 0)
+ continue;
+
+ var z = new Number.double (a).reciprocal ();
+ var expected = 1.0 / a;
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).reciprocal () -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+
+ z = new Number.double (expected).reciprocal ();
+ if (!double_matches (z, a))
+ {
+ fail ("(%f).reciprocal () -> %f, expected %f".printf (expected, z.to_double (), a));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_epowy ()
+{
+ for (var ai = -100; ai <= 100; ai++)
+ {
+ var a = ai / 10.0;
+ var z = new Number.double (a).epowy ();
+ var expected = Math.exp (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).epowy () -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_xpowy ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ var z = new Number.integer (a).xpowy (new Number.integer (b));
+ var expected = 1.0;
+ if (a == 0)
+ {
+ if (b != 0)
+ expected = 0.0;
+ }
+ else
+ {
+ if (b < 0)
+ for (var i = 0; i > b; i--)
+ expected /= a;
+ else if (b > 0)
+ for (var i = 0; i < b; i++)
+ expected *= a;
+ }
+
+ if (!double_matches (z, expected))
+ {
+ fail ("(%d).xpowy (%d) -> %f, expected %f".printf (a, b, z.to_double (), expected));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+private void test_xpowy_integer ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ var z = new Number.integer (a).xpowy_integer (b);
+ var expected = 1.0;
+ if (a == 0)
+ {
+ if (b != 0)
+ expected = 0.0;
+ }
+ else
+ {
+ if (b < 0)
+ for (var i = 0; i > b; i--)
+ expected /= a;
+ else if (b > 0)
+ for (var i = 0; i < b; i++)
+ expected *= a;
+ }
+
+ if (!double_matches (z, expected))
+ {
+ fail ("(%d).xpowy_integer (%d) -> %f, expected %f".printf (a, b, z.to_double (), expected));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+private void test_root3 ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ var z = new Number.double (a).root (3);
+ var expected = Math.cbrt (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).root (3) -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_sqrt ()
+{
+ for (var a = 0; a <= 10; a++)
+ {
+ var z = new Number.double (a).sqrt ();
+ var expected = Math.sqrt (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).sqrt () -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_ln ()
+{
+ for (var a = 1; a <= 10; a++)
+ {
+ var z = new Number.double (a).ln ();
+ var expected = Math.log (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).ln () -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_logarithm2 ()
+{
+ for (var a = 1; a <= 10; a++)
+ {
+ var z = new Number.double (a).logarithm (2);
+ var expected = Math.log2 (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).logarithm (2) -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_logarithm10 ()
+{
+ for (var a = 1; a <= 10; a++)
+ {
+ var z = new Number.double (a).logarithm (10);
+ var expected = Math.log10 (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).logarithm (10) -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_is_zero ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ var z = new Number.integer (a);
+ var expected = a == 0;
+ if (z.is_zero () != expected)
+ {
+ fail ("(%d).is_zero () -> %s, expected %s".printf (a, z.is_zero () ? "true" : "false", expected ? "true" : "false"));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_is_negative ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ var z = new Number.integer (a);
+ var expected = a < 0;
+ if (z.is_negative () != expected)
+ {
+ fail ("(%d).is_negative () -> %s, expected %s".printf (a, z.is_negative () ? "true" : "false", expected ? "true" : "false"));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_is_integer ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ var z = new Number.integer (a);
+ var expected = true;
+ if (z.is_integer () != expected)
+ {
+ fail ("(%d).is_integer () -> %s, expected %s".printf (a, z.is_integer () ? "true" : "false", expected ? "true" : "false"));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_is_positive_integer ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ var z = new Number.integer (a);
+ var expected = a >= 0;
+ if (z.is_positive_integer () != expected)
+ {
+ fail ("(%d).is_positive_integer () -> %s, expected %s".printf (a, z.is_positive_integer () ? "true" : "false", expected ? "true" : "false"));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_is_natural ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ var z = new Number.integer (a);
+ var expected = a > 0;
+ if (z.is_natural () != expected)
+ {
+ fail ("(%d).is_natural () -> %s, expected %s".printf (a, z.is_natural () ? "true" : "false", expected ? "true" : "false"));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_is_complex ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ var z = new Number.complex (new Number.integer (a), new Number.integer (b));
+ var expected = b != 0;
+ if (z.is_complex () != expected)
+ {
+ fail ("(%d+%di).is_complex () -> %s, expected %s".printf (a, b, z.is_complex () ? "true" : "false", expected ? "true" : "false"));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+private void test_factorial ()
+{
+ for (var a = 0; a <= 10; a++)
+ {
+ var z = new Number.integer (a);
+ var expected = 1;
+ for (var i = 2; i <= a; i++)
+ expected *= i;
+ if (z.factorial ().to_integer () != expected)
+ {
+ fail ("(%d).factorial () -> %lli, expected %lli".printf (a, z.factorial ().to_integer (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+private void test_add ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ var z = (new Number.integer (a)).add (new Number.integer (b));
+ var expected = a + b;
+ if (z.to_integer () != expected)
+ {
+ fail ("(%d).add (%d) -> %lli, expected %d".printf (a, b, z.to_integer (), expected));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+private void test_subtract ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ var z = (new Number.integer (a)).subtract (new Number.integer (b));
+ var expected = a - b;
+ if (z.to_integer () != expected)
+ {
+ fail ("(%d).subtract (%d) -> %lli, expected %d".printf (a, b, z.to_integer (), expected));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+private void test_multiply ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ var z = (new Number.integer (a)).multiply (new Number.integer (b));
+ var expected = a * b;
+ if (z.to_integer () != expected)
+ {
+ fail ("(%d).multiply (%d) -> %lli, expected %d".printf (a, b, z.to_integer (), expected));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+private void test_multiply_integer ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ var z = (new Number.integer (a)).multiply_integer (b);
+ var expected = a * b;
+ if (z.to_integer () != expected)
+ {
+ fail ("(%d).multiply_integer (%d) -> %lli, expected %d".printf (a, b, z.to_integer (), expected));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+private void test_divide ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ if (b == 0)
+ continue;
+
+ var z = (new Number.integer (a * b)).divide (new Number.integer (b));
+ var expected = a;
+ if (z.to_integer () != expected)
+ {
+ fail ("(%d).divide (%d) -> %lli, expected %d".printf (a * b, b, z.to_integer (), expected));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+private void test_divide_integer ()
+{
+ for (var a = -10; a <= 10; a++)
+ {
+ for (var b = -10; b <= 10; b++)
+ {
+ if (b == 0)
+ continue;
+
+ var z = (new Number.integer (a * b)).divide_integer (b);
+ var expected = a;
+ if (z.to_integer () != expected)
+ {
+ fail ("(%d).divide_integer (%d) -> %lli, expected %d".printf (a * b, b, z.to_integer (), expected));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+private void test_modulus_divide ()
+{
+ for (var a = 0; a <= 10; a++)
+ {
+ for (var b = 1; b <= 10; b++)
+ {
+ var z = (new Number.integer (a)).modulus_divide (new Number.integer (b));
+ var expected = a % b;
+ if (z.to_integer () != expected)
+ {
+ fail ("(%d).modulus_divide (%d) -> %lli, expected %d".printf (a, b, z.to_integer (), expected));
+ return;
+ }
+ }
+ }
+
+ pass ();
+}
+
+static void test_sin ()
+{
+ for (var a = -Math.PI; a <= Math.PI; a += Math.PI / 16)
+ {
+ var z = new Number.double (a).sin ();
+ var expected = Math.sin (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).sin () -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+static void test_cos ()
+{
+ for (var a = -Math.PI; a <= Math.PI; a += Math.PI / 16)
+ {
+ var z = new Number.double (a).cos ();
+ var expected = Math.cos (a);
+ if (!double_matches (z, expected))
+ {
+ fail ("(%f).cos () -> %f, expected %f".printf (a, z.to_double (), expected));
+ return;
+ }
+ }
+
+ pass ();
+}
+
+static int main (string[] args)
+{
+ Intl.setlocale (LocaleCategory.ALL, "C");
+
+ test_integer ();
+ test_unsigned_integer ();
+ test_fraction ();
+ test_float ();
+ test_double ();
+ test_complex ();
+ test_polar ();
+ test_string ();
+ test_eulers ();
+ test_i ();
+ test_pi ();
+ //test_random ();
+ test_is_zero ();
+ test_is_negative ();
+ test_is_integer ();
+ test_is_positive_integer ();
+ test_is_natural ();
+ test_is_complex ();
+ test_sgn ();
+ test_invert_sign ();
+ test_abs ();
+ test_arg ();
+ test_conjugate ();
+ test_real_component ();
+ test_imaginary_component ();
+ test_integer_component ();
+ test_fractional_component ();
+ test_fractional_part ();
+ test_floor ();
+ test_ceiling ();
+ test_round ();
+ test_reciprocal ();
+ test_epowy ();
+ test_xpowy ();
+ test_xpowy_integer ();
+ test_root3 (); // FIXME: should check other roots
+ test_sqrt ();
+ test_ln ();
+ test_logarithm2 (); // FIXME: Should check other bases
+ test_logarithm10 (); // FIXME: Should check other bases
+ test_factorial ();
+ test_add ();
+ test_subtract ();
+ test_multiply ();
+ test_multiply_integer ();
+ test_divide ();
+ test_divide_integer ();
+ test_modulus_divide ();
+ test_sin ();
+ test_cos ();
+ //test_tan ();
+ //test_asin ();
+ //test_acos ();
+ //test_atan ();
+ //test_sinh ();
+ //test_cosh ();
+ //test_tanh ();
+ //test_asinh ();
+ //test_acosh ();
+ //test_atanh ();
+ //test_and ();
+ //test_or ();
+ //test_xor ();
+ //test_not ();
+ //test_mask ();
+ //test_shift ();
+ //test_ones_complement ();
+ //test_twos_complement ();
+ //test_factorize ();
+
+ if (fail_count == 0)
+ stdout.printf ("Passed all %i tests\n", pass_count);
+ else
+ stdout.printf ("Failed %i/%d tests\n", fail_count, pass_count + fail_count);
+
+ return fail_count;
+}
diff --git a/src/unit.vala b/src/unit.vala
new file mode 100644
index 0000000..fec4078
--- /dev/null
+++ b/src/unit.vala
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2008-2012 Robert Ancell.
+ *
+ * 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. See http://www.gnu.org/copyleft/gpl.html the full text of the
+ * license.
+ */
+
+private UnitManager? default_unit_manager = null;
+
+public class UnitManager : Object
+{
+ private List<UnitCategory> categories;
+
+ public UnitManager ()
+ {
+ categories = new List<UnitCategory> ();
+ }
+
+ public static UnitManager get_default ()
+ {
+ if (default_unit_manager != null)
+ return default_unit_manager;
+
+ default_unit_manager = new UnitManager ();
+
+ var angle_category = default_unit_manager.add_category ("angle", _("Angle"));
+ var length_category = default_unit_manager.add_category ("length", _("Length"));
+ var area_category = default_unit_manager.add_category ("area", _("Area"));
+ var volume_category = default_unit_manager.add_category ("volume", _("Volume"));
+ var weight_category = default_unit_manager.add_category ("weight", _("Weight"));
+ var duration_category = default_unit_manager.add_category ("duration", _("Duration"));
+ var temperature_category = default_unit_manager.add_category ("temperature", _("Temperature"));
+
+ /* FIXME: Approximations of 1/(units in a circle), therefore, 360 deg != 400 grads */
+ angle_category.add_unit (new Unit ("degree", _("Degrees"), dpgettext2 (null, "unit-format", "%s degrees"), "Ï*x/180", "180x/Ï", dpgettext2 (null, "unit-symbols", "degree,degrees,deg")));
+ angle_category.add_unit (new Unit ("radian", _("Radians"), dpgettext2 (null, "unit-format", "%s radians"), "x", "x", dpgettext2 (null, "unit-symbols", "radian,radians,rad")));
+ angle_category.add_unit (new Unit ("gradian", _("Gradians"), dpgettext2 (null, "unit-format", "%s gradians"), "Ï*x/200", "200x/Ï", dpgettext2 (null, "unit-symbols", "gradian,gradians,grad")));
+ length_category.add_unit (new Unit ("parsec", _("Parsecs"), dpgettext2 (null, "unit-format", "%s pc"), "30857000000000000x", "x/30857000000000000", dpgettext2 (null, "unit-symbols", "parsec,parsecs,pc")));
+ length_category.add_unit (new Unit ("lightyear", _("Light Years"), dpgettext2 (null, "unit-format", "%s ly"), "9460730472580800x", "x/9460730472580800", dpgettext2 (null, "unit-symbols", "lightyear,lightyears,ly")));
+ length_category.add_unit (new Unit ("astronomical-unit", _("Astronomical Units"), dpgettext2 (null, "unit-format", "%s au"), "149597870691x", "x/149597870691", dpgettext2 (null, "unit-symbols", "au")));
+ length_category.add_unit (new Unit ("nautical-mile", _("Nautical Miles"), dpgettext2 (null, "unit-format", "%s nmi"), "1852x", "x/1852", dpgettext2 (null, "unit-symbols", "nmi")));
+ length_category.add_unit (new Unit ("mile", _("Miles"), dpgettext2 (null, "unit-format", "%s mi"), "1609.344x", "x/1609.344", dpgettext2 (null, "unit-symbols", "mile,miles,mi")));
+ length_category.add_unit (new Unit ("kilometer", _("Kilometers"), dpgettext2 (null, "unit-format", "%s km"), "1000x", "x/1000", dpgettext2 (null, "unit-symbols", "kilometer,kilometers,km,kms")));
+ length_category.add_unit (new Unit ("cable", _("Cables"), dpgettext2 (null, "unit-format", "%s cb"), "219.456x", "x/219.456", dpgettext2 (null, "unit-symbols", "cable,cables,cb")));
+ length_category.add_unit (new Unit ("fathom", _("Fathoms"), dpgettext2 (null, "unit-format", "%s ftm"), "1.8288x", "x/1.8288", dpgettext2 (null, "unit-symbols", "fathom,fathoms,ftm")));
+ length_category.add_unit (new Unit ("meter", _("Meters"), dpgettext2 (null, "unit-format", "%s m"), "x", "x", dpgettext2 (null, "unit-symbols", "meter,meters,m")));
+ length_category.add_unit (new Unit ("yard", _("Yards"), dpgettext2 (null, "unit-format", "%s yd"), "0.9144x", "x/0.9144", dpgettext2 (null, "unit-symbols", "yard,yards,yd")));
+ length_category.add_unit (new Unit ("foot", _("Feet"), dpgettext2 (null, "unit-format", "%s ft"), "0.3048x", "x/0.3048", dpgettext2 (null, "unit-symbols", "foot,feet,ft")));
+ length_category.add_unit (new Unit ("inch", _("Inches"), dpgettext2 (null, "unit-format", "%s in"), "0.0254x", "x/0.0254", dpgettext2 (null, "unit-symbols", "inch,inches,in")));
+ length_category.add_unit (new Unit ("centimeter", _("Centimeters"), dpgettext2 (null, "unit-format", "%s cm"), "x/100", "100x", dpgettext2 (null, "unit-symbols", "centimeter,centimeters,cm,cms")));
+ length_category.add_unit (new Unit ("millimeter", _("Millimeters"), dpgettext2 (null, "unit-format", "%s mm"), "x/1000", "1000x", dpgettext2 (null, "unit-symbols", "millimeter,millimeters,mm")));
+ length_category.add_unit (new Unit ("micrometer", _("Micrometers"), dpgettext2 (null, "unit-format", "%s Îm"), "x/1000000", "1000000x", dpgettext2 (null, "unit-symbols", "micrometer,micrometers,um")));
+ length_category.add_unit (new Unit ("nanometer", _("Nanometers"), dpgettext2 (null, "unit-format", "%s nm"), "x/1000000000", "1000000000x", dpgettext2 (null, "unit-symbols", "nanometer,nanometers,nm")));
+ area_category.add_unit (new Unit ("hectare", _("Hectares"), dpgettext2 (null, "unit-format", "%s ha"), "10000x", "x/10000", dpgettext2 (null, "unit-symbols", "hectare,hectares,ha")));
+ area_category.add_unit (new Unit ("acre", _("Acres"), dpgettext2 (null, "unit-format", "%s acres"), "4046.8564224x", "x/4046.8564224", dpgettext2 (null, "unit-symbols", "acre,acres")));
+ area_category.add_unit (new Unit ("square-meter", _("Square Meters"), dpgettext2 (null, "unit-format", "%s mÂ"), "x", "x", dpgettext2 (null, "unit-symbols", "mÂ")));
+ area_category.add_unit (new Unit ("square-centimeter", _("Square Centimeters"), dpgettext2 (null, "unit-format", "%s cmÂ"), "0.0001x", "10000x", dpgettext2 (null, "unit-symbols", "cmÂ")));
+ area_category.add_unit (new Unit ("square-millimeter", _("Square Millimeters"), dpgettext2 (null, "unit-format", "%s mmÂ"), "0.000001x", "1000000x", dpgettext2 (null, "unit-symbols", "mmÂ")));
+ volume_category.add_unit (new Unit ("cubic-meter", _("Cubic Meters"), dpgettext2 (null, "unit-format", "%s mÂ"), "1000x", "x/1000", dpgettext2 (null, "unit-symbols", "mÂ")));
+ volume_category.add_unit (new Unit ("gallon", _("Gallons"), dpgettext2 (null, "unit-format", "%s gal"), "3.785412x", "x/3.785412", dpgettext2 (null, "unit-symbols", "gallon,gallons,gal")));
+ volume_category.add_unit (new Unit ("litre", _("Litres"), dpgettext2 (null, "unit-format", "%s L"), "x", "x", dpgettext2 (null, "unit-symbols", "litre,litres,liter,liters,L")));
+ volume_category.add_unit (new Unit ("quart", _("Quarts"), dpgettext2 (null, "unit-format", "%s qt"), "0.9463529x", "x/0.9463529", dpgettext2 (null, "unit-symbols", "quart,quarts,qt")));
+ volume_category.add_unit (new Unit ("pint", _("Pints"), dpgettext2 (null, "unit-format", "%s pt"), "0.4731765x", "x/0.4731765", dpgettext2 (null, "unit-symbols", "pint,pints,pt")));
+ volume_category.add_unit (new Unit ("millilitre", _("Millilitres"), dpgettext2 (null, "unit-format", "%s mL"), "0.001x", "1000x", dpgettext2 (null, "unit-symbols", "millilitre,millilitres,milliliter,milliliters,mL,cmÂ")));
+ volume_category.add_unit (new Unit ("microlitre", _("Microlitres"), dpgettext2 (null, "unit-format", "%s ÎL"), "0.000001x", "1000000x", dpgettext2 (null, "unit-symbols", "mmÂ,ÎL,uL")));
+ weight_category.add_unit (new Unit ("tonne", _("Tonnes"), dpgettext2 (null, "unit-format", "%s T"), "1000x", "x/1000", dpgettext2 (null, "unit-symbols", "tonne,tonnes")));
+ weight_category.add_unit (new Unit ("kilograms", _("Kilograms"), dpgettext2 (null, "unit-format", "%s kg"), "x", "x", dpgettext2 (null, "unit-symbols", "kilogram,kilograms,kilogramme,kilogrammes,kg,kgs")));
+ weight_category.add_unit (new Unit ("pound", _("Pounds"), dpgettext2 (null, "unit-format", "%s lb"), "0.45359237x", "x/0.45359237", dpgettext2 (null, "unit-symbols", "pound,pounds,lb")));
+ weight_category.add_unit (new Unit ("ounce", _("Ounces"), dpgettext2 (null, "unit-format", "%s oz"), "0.02834952x", "x/0.02834952", dpgettext2 (null, "unit-symbols", "ounce,ounces,oz")));
+ weight_category.add_unit (new Unit ("gram", _("Grams"), dpgettext2 (null, "unit-format", "%s g"), "0.001x", "1000x", dpgettext2 (null, "unit-symbols", "gram,grams,gramme,grammes,g")));
+ duration_category.add_unit (new Unit ("year", _("Years"), dpgettext2 (null, "unit-format", "%s years"), "31557600x", "x/31557600", dpgettext2 (null, "unit-symbols", "year,years")));
+ duration_category.add_unit (new Unit ("day", _("Days"), dpgettext2 (null, "unit-format", "%s days"), "86400x", "x/86400", dpgettext2 (null, "unit-symbols", "day,days")));
+ duration_category.add_unit (new Unit ("hour", _("Hours"), dpgettext2 (null, "unit-format", "%s hours"), "3600x", "x/3600", dpgettext2 (null, "unit-symbols", "hour,hours")));
+ duration_category.add_unit (new Unit ("minute", _("Minutes"), dpgettext2 (null, "unit-format", "%s minutes"), "60x", "x/60", dpgettext2 (null, "unit-symbols", "minute,minutes")));
+ duration_category.add_unit (new Unit ("second", _("Seconds"), dpgettext2 (null, "unit-format", "%s s"), "x", "x", dpgettext2 (null, "unit-symbols", "second,seconds,s")));
+ duration_category.add_unit (new Unit ("millisecond", _("Milliseconds"), dpgettext2 (null, "unit-format", "%s ms"), "0.001x", "1000x", dpgettext2 (null, "unit-symbols", "millisecond,milliseconds,ms")));
+ duration_category.add_unit (new Unit ("microsecond", _("Microseconds"), dpgettext2 (null, "unit-format", "%s Îs"), "0.000001x", "1000000x", dpgettext2 (null, "unit-symbols", "microsecond,microseconds,us,Îs")));
+ temperature_category.add_unit (new Unit ("degree-celcius", _("Celsius"), dpgettext2 (null, "unit-format", "%s ËC"), "x+273.15", "x-273.15", dpgettext2 (null, "unit-symbols", "degC,ËC")));
+ temperature_category.add_unit (new Unit ("degree-farenheit", _("Farenheit"), dpgettext2 (null, "unit-format", "%s ËF"), "(x+459.67)*5/9", "x*9/5-459.67", dpgettext2 (null, "unit-symbols", "degF,ËF")));
+ temperature_category.add_unit (new Unit ("degree-kelvin", _("Kelvin"), dpgettext2 (null, "unit-format", "%s K"), "x", "x", dpgettext2 (null, "unit-symbols", "K")));
+ temperature_category.add_unit (new Unit ("degree-rankine", _("Rankine"), dpgettext2 (null, "unit-format", "%s ËR"), "x*5/9", "x*9/5", dpgettext2 (null, "unit-symbols", "degR,ËR,ËRa")));
+
+ var currency_category = default_unit_manager.add_category ("currency", _("Currency"));
+ var currencies = CurrencyManager.get_default ().get_currencies ();
+ currencies.sort ((a, b) => { return strcmp (a.display_name, b.display_name); });
+ foreach (var currency in currencies)
+ {
+ /* Translators: result of currency conversion, %s is the symbol, %%s is the placeholder for amount, i.e.: USD100 */
+ var format = _("%s%%s").printf (currency.symbol);
+ var unit = new Unit (currency.name, currency.display_name, format, null, null, currency.name);
+ currency_category.add_unit ( unit);
+ }
+
+ return default_unit_manager;
+ }
+
+ public UnitCategory add_category (string name, string display_name)
+ {
+ var category = new UnitCategory (name, display_name);
+ categories.append (category);
+ return category;
+ }
+
+ public List<UnitCategory> get_categories ()
+ {
+ var r = new List<UnitCategory> ();
+ foreach (var c in categories)
+ r.append (c);
+ return r;
+ }
+
+ public UnitCategory? get_category (string category)
+ {
+ foreach (var c in categories)
+ if (c.name == category)
+ return c;
+
+ return null;
+ }
+
+ public Unit? get_unit_by_name (string name)
+ {
+ foreach (var c in categories)
+ {
+ var u = c.get_unit_by_name (name);
+ if (u != null)
+ return u;
+ }
+
+ return null;
+ }
+
+ public Unit? get_unit_by_symbol (string symbol)
+ {
+ foreach (var c in categories)
+ {
+ var u = c.get_unit_by_symbol (symbol);
+ if (u != null)
+ return u;
+ }
+
+ return null;
+ }
+
+ public Number? convert_by_symbol (Number x, string x_symbol, string z_symbol)
+ {
+ foreach (var c in categories)
+ {
+ var x_units = c.get_unit_by_symbol (x_symbol);
+ var z_units = c.get_unit_by_symbol (z_symbol);
+ if (x_units != null && z_units != null)
+ return c.convert (x, x_units, z_units);
+ }
+
+ return null;
+ }
+}
+
+public class UnitCategory : Object
+{
+ private List<Unit> units;
+
+ private string _name;
+ public string name { owned get { return _name; } }
+
+ private string _display_name;
+ public string display_name { owned get { return _display_name; } }
+
+ public UnitCategory (string name, string display_name)
+ {
+ _name = name;
+ _display_name = display_name;
+ units = new List<Unit> ();
+ }
+
+ public void add_unit (Unit unit)
+ {
+ units.append (unit);
+ }
+
+ public Unit? get_unit_by_name (string name)
+ {
+ foreach (var unit in units)
+ if (unit.name == name)
+ return unit;
+
+ return null;
+ }
+
+ public Unit? get_unit_by_symbol (string symbol)
+ {
+ foreach (var unit in units)
+ if (unit.matches_symbol (symbol))
+ return unit;
+
+ return null;
+ }
+
+ public unowned List<Unit> get_units ()
+ {
+ return units;
+ }
+
+ public Number? convert (Number x, Unit x_units, Unit z_units)
+ {
+ var t = x_units.convert_from (x);
+ if (t == null)
+ return null;
+ return z_units.convert_to (t);
+ }
+}
+
+public class Unit : Object
+{
+ private string _name;
+ public string name { owned get { return _name; } }
+
+ private string _display_name;
+ public string display_name { owned get { return _display_name; } }
+
+ private string _format;
+ private List<string> _symbols;
+ private string? from_function;
+ private string? to_function;
+ private Serializer serializer;
+
+ public Unit (string name, string display_name, string format, string? from_function, string? to_function, string symbols)
+ {
+ serializer = new Serializer (DisplayFormat.AUTOMATIC, 10, 2);
+ serializer.set_leading_digits (6);
+
+ _name = name;
+ _display_name = display_name;
+ this._format = format;
+ this.from_function = from_function;
+ this.to_function = to_function;
+ _symbols = new List<string> ();
+ var symbol_names = symbols.split (",", 0);
+ foreach (var symbol_name in symbol_names)
+ _symbols.append (symbol_name);
+ }
+
+ public bool matches_symbol (string symbol)
+ {
+ foreach (var s in _symbols)
+ if (s == symbol)
+ return true;
+
+ return false;
+ }
+
+ public unowned List<string> get_symbols ()
+ {
+ return _symbols;
+ }
+
+ public Number? convert_from (Number x)
+ {
+ if (from_function != null)
+ return solve_function (from_function, x);
+ else
+ {
+ // FIXME: Hack to make currency work
+ var r = CurrencyManager.get_default ().get_value (name);
+ if (r == null)
+ return null;
+ return x.divide (r);
+ }
+ }
+
+ public Number? convert_to (Number x)
+ {
+ if (to_function != null)
+ return solve_function (to_function, x);
+ else
+ {
+ // FIXME: Hack to make currency work
+ var r = CurrencyManager.get_default ().get_value (name);
+ if (r == null)
+ return null;
+ return x.multiply (r);
+ }
+ }
+
+ public string format (Number x)
+ {
+ var number_text = serializer.to_string (x);
+ return _format.printf (number_text);
+ }
+
+ private Number? solve_function (string function, Number x)
+ {
+ var equation = new UnitSolveEquation (function, x);
+ equation.base = 10;
+ equation.wordlen = 32;
+
+ var z = equation.parse ();
+ if (z == null)
+ warning ("Failed to convert value: %s", function);
+
+ return z;
+ }
+}
+
+private class UnitSolveEquation : Equation
+{
+ private Number x;
+
+ public UnitSolveEquation (string function, Number x)
+ {
+ base (function);
+ this.x = x;
+ }
+
+ public override bool variable_is_defined (string name)
+ {
+ return true;
+ }
+
+ public override Number? get_variable (string name)
+ {
+ return x;
+ }
+}
\ No newline at end of file
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]