[gcalctool] Add string serializer class from parts of mp-convert and math-equation



commit 492223c3f9e503686d62e74c48b994e7588f2cee
Author: Robin Sonefors <ozamosi flukkost nu>
Date:   Mon Oct 4 21:51:20 2010 +0200

    Add string serializer class from parts of mp-convert and math-equation
    
    Put back thousands separators support, fixes #628908

 configure.ac              |    4 +
 src/Makefile.am           |   21 ++-
 src/gcalccmd.c            |    7 +-
 src/gcalctool.c           |    4 +-
 src/math-buttons.c        |   22 ++-
 src/math-enums.c.template |   36 ++++
 src/math-enums.h.template |   25 +++
 src/math-equation.c       |  163 ++++++---------
 src/math-equation.h       |   12 +-
 src/math-variables.c      |    3 +-
 src/mp-binary.c           |    7 +-
 src/mp-convert.c          |  247 ++---------------------
 src/mp-equation-lexer.l   |    1 -
 src/mp-serializer.c       |  508 +++++++++++++++++++++++++++++++++++++++++++++
 src/mp-serializer.h       |   72 +++++++
 src/mp.h                  |   18 --
 src/unittest.c            |    3 +-
 17 files changed, 770 insertions(+), 383 deletions(-)
---
diff --git a/configure.ac b/configure.ac
index cf8b052..3ae6096 100644
--- a/configure.ac
+++ b/configure.ac
@@ -49,8 +49,12 @@ PKG_CHECK_MODULES(GCALCTOOL, [
 
 PKG_CHECK_MODULES(GCALCCMD, [
     glib-2.0
+    gobject-2.0
 ])
 
+GLIB_MKENUMS=`$PKG_CONFIG --variable=glib_mkenums glib-2.0`
+AC_SUBST(GLIB_MKENUMS)
+
 AC_CHECK_LIB(m, log)
 
 dnl ###########################################################################
diff --git a/src/Makefile.am b/src/Makefile.am
index 6875200..db74fe0 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -24,6 +24,8 @@ gcalctool_SOURCES = \
 	math-variables.h \
 	math-window.c \
 	math-window.h \
+	math-enums.c \
+	math-enums.h \
 	mp.c \
 	mp.h \
 	mp-binary.c \
@@ -37,6 +39,8 @@ gcalctool_SOURCES = \
 	mp-equation-lexer.h \
 	mp-equation-parser.c \
 	mp-equation-parser.h \
+	mp-serializer.c \
+	mp-serializer.h \
 	financial.c \
 	financial.h \
 	unittest.c \
@@ -53,7 +57,11 @@ gcalccmd_SOURCES = \
 	mp-trigonometric.c \
 	mp-equation.c \
 	mp-equation-parser.c \
-	mp-equation-lexer.c
+	mp-equation-lexer.c \
+	mp-serializer.c \
+	mp-serializer.h\
+	math-enums.c \
+	math-enums.h
 
 gcalccmd_LDADD = \
 	$(GCALCCMD_LIBS) \
@@ -63,7 +71,9 @@ CLEANFILES = \
 	mp-equation-parser.h \
 	mp-equation-parser.c \
 	mp-equation-lexer.c \
-	mp-equation-lexer.h
+	mp-equation-lexer.h \
+	math-enums.c \
+	math-enums.h
 
 # Generate parser files
 mp-equation-parser.c mp-equation-parser.h: mp-equation-parser.y mp-equation-lexer.h
@@ -78,6 +88,13 @@ mp-equation-parser.o: mp-equation-lexer.h
 mp-equation-lexer.o: mp-equation-parser.h
 mp-equation.c: mp-equation-lexer.h mp-equation-parser.h
 
+# Generate enum types
+math-enums.h: math-enums.h.template
+	$(AM_V_GEN)$(GLIB_MKENUMS) --template $(srcdir)/math-enums.h.template $(srcdir)/mp-serializer.h > math-enums.h
+
+math-enums.c: math-enums.c.template math-enums.h
+	$(AM_V_GEN)$(GLIB_MKENUMS) --template $(srcdir)/math-enums.c.template $(srcdir)/mp-serializer.h > math-enums.c
+
 # Install a symlink between gcalctool and gnome-calculator
 install-exec-hook:
 	test -e "$(DESTDIR)$(bindir)/gnome-calculator" \
diff --git a/src/gcalccmd.c b/src/gcalccmd.c
index 2812952..e7a6509 100644
--- a/src/gcalccmd.c
+++ b/src/gcalccmd.c
@@ -23,8 +23,10 @@
 #include <string.h>
 #include <sys/types.h>
 #include <time.h>
+#include <locale.h>
 
 #include "mp-equation.h"
+#include "mp-serializer.h"
 
 #define MAXLINE 1024
 
@@ -48,7 +50,7 @@ solve(const char *equation)
     else if (ret)        
         fprintf(stderr, "Error %d\n", ret);
     else {
-        mp_cast_to_string(&z, 10, 10, 9, 1, TRUE, result_str, MAXLINE);
+        mp_serializer_to_specific_string(&z, 10, 9, true, true, result_str, MAXLINE);
         printf("%s\n", result_str);
     }
 }
@@ -79,6 +81,9 @@ main(int argc, char **argv)
     /* Seed random number generator. */
     srand48((long) time((time_t *) 0));
 
+    g_type_init ();
+    setlocale(LC_NUMERIC, "");
+
     equation = (char *) malloc(MAXLINE * sizeof(char));
     while (1) {
         printf("> ");
diff --git a/src/gcalctool.c b/src/gcalctool.c
index e2d8b45..1bb4f87 100644
--- a/src/gcalctool.c
+++ b/src/gcalctool.c
@@ -20,6 +20,7 @@
 #include <stdlib.h>
 #include <stdio.h>
 #include <string.h>
+#include <locale.h>
 
 #include "currency.h"
 #include "unittest.h"
@@ -61,7 +62,7 @@ solve(const char *equation)
         exit(1);
     }
     else {
-        mp_cast_to_string(&result, 10, 10, 9, 1, TRUE, result_str, 1024);
+        mp_serializer_to_specific_string(&result, options.base, 9, true, true, result_str, 1024);
         printf("%s\n", result_str);
         exit(0);
     }
@@ -212,6 +213,7 @@ main(int argc, char **argv)
     gchar *source_currency, *target_currency;
 
     g_type_init();
+    setlocale(LC_NUMERIC, "");
 
     bindtextdomain(GETTEXT_PACKAGE, LOCALE_DIR);
     bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
diff --git a/src/math-buttons.c b/src/math-buttons.c
index 81b3057..ab0b200 100644
--- a/src/math-buttons.c
+++ b/src/math-buttons.c
@@ -21,6 +21,7 @@
 #include "math-buttons.h"
 #include "financial.h"
 #include "currency.h"
+#include "mp-serializer.h"
 
 enum {
     PROP_0,
@@ -422,10 +423,10 @@ update_angle_label (MathButtons *buttons)
             mp_set_from_mp(&x, &input);
             mp_set_from_integer(mp_is_negative(&input) ? -1 : 1, &fraction);
         }
-        mp_cast_to_string(&input, 10, 10, 2, false, true, input_text, 1024);
+        mp_serializer_to_specific_string(&input, 10, 2, false, true, input_text, 1024);
 
         mp_multiply_integer(&fraction, 360, &output);
-        mp_cast_to_string(&output, 10, 10, 2, false, true, output_text, 1024);
+        mp_serializer_to_specific_string(&output, 10, 2, false, true, output_text, 1024);
         label = g_strdup_printf(_("%s radians = %s degrees"), input_text, output_text);
         break;
     case MP_GRADIANS:
@@ -442,10 +443,10 @@ update_angle_label (MathButtons *buttons)
             mp_set_from_integer(mp_is_negative(&input) ? -1 : 1, &fraction);
         }
 
-        mp_cast_to_string(&input, 10, 10, 2, false, true, input_text, 1024);
+        mp_serializer_to_specific_string(&input, 10, 2, false, true, input_text, 1024);
 
         mp_multiply_integer(&fraction, 360, &output);
-        mp_cast_to_string(&output, 10, 10, 2, false, true, output_text, 1024);
+        mp_serializer_to_specific_string(&output, 10, 2, false, true, output_text, 1024);
         label = g_strdup_printf(_("%s gradians = %s degrees"), input_text, output_text);
         break;
     }
@@ -543,8 +544,8 @@ update_currency_label(MathButtons *buttons)
         const char *source_symbol, *target_symbol;
         int i;
 
-        mp_cast_to_string(&x, 10, 10, 2, false, true, input_text, 1024);
-        mp_cast_to_string(&value, 10, 10, 2, false, true, output_text, 1024);
+        mp_serializer_to_specific_string(&x, 10, 2, false, true, input_text, 1024);
+        mp_serializer_to_specific_string(&value, 10, 2, false, true, output_text, 1024);
 
         for (i = 0; strcmp(math_equation_get_source_currency(buttons->priv->equation), currency_names[i].short_name) != 0; i++);
         source_symbol = currency_names[i].symbol;
@@ -861,8 +862,10 @@ load_mode(MathButtons *buttons, ButtonMode mode)
         g_free(name);
     }
     widget = GET_WIDGET(builder, "calc_numeric_point_button");
-    if (widget)
-        gtk_button_set_label(GTK_BUTTON(widget), math_equation_get_numeric_point_text(buttons->priv->equation));
+    if (widget) {
+        MpSerializer *serializer = math_equation_get_serializer(buttons->priv->equation);
+        gtk_button_set_label(GTK_BUTTON(widget), mp_serializer_get_numeric_point_text(serializer));
+    }
   
     widget = GET_WIDGET(builder, "calc_superscript_button");
     if (widget) {
@@ -1194,7 +1197,8 @@ make_register_menu_item(MathButtons *buttons, const gchar *name, const MPNumber
     GtkWidget *item, *label;
 
     if (value) {
-        display_make_number(buttons->priv->equation, text, 1024, value);
+        MpSerializer *serializer = math_equation_get_serializer(buttons->priv->equation);
+        mp_serializer_to_standard_string(serializer, value, text, 1024);
         mstr = g_strdup_printf("<span weight=\"bold\">%s</span> = %s", name, text);
     }
     else
diff --git a/src/math-enums.c.template b/src/math-enums.c.template
new file mode 100644
index 0000000..3b83c71
--- /dev/null
+++ b/src/math-enums.c.template
@@ -0,0 +1,36 @@
+/*** BEGIN file-header ***/
+#include "mp-serializer.h"
+#include "math-enums.h"
+
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType
+math_ enum_name@_get_type (void)
+{
+    static GType etype = 0;
+    if (G_UNLIKELY(etype == 0)) {
+        static const G Type@Value values[] = {
+/*** END value-header ***/
+
+/*** BEGIN value-production ***/
+            { @VALUENAME@, "@VALUENAME@", "@valuenick@" },
+/*** END value-production ***/
+
+/*** BEGIN value-tail ***/
+            { 0, NULL, NULL }
+        };
+        etype = g_ type@_register_static (g_intern_static_string ("@EnumName@"), values);
+    }
+    return etype;
+}
+
+/*** END value-tail ***/
+
+/*** BEGIN file-tail ***/
+
+/*** END file-tail ***/
diff --git a/src/math-enums.h.template b/src/math-enums.h.template
new file mode 100644
index 0000000..f192bf8
--- /dev/null
+++ b/src/math-enums.h.template
@@ -0,0 +1,25 @@
+/*** BEGIN file-header ***/
+
+#ifndef __MATH_ENUMS_H__
+#define __MATH_ENUMS_H__
+
+#include <glib-object.h>
+
+G_BEGIN_DECLS
+/*** END file-header ***/
+
+/*** BEGIN file-production ***/
+
+/* enumerations from "@filename@" */
+/*** END file-production ***/
+
+/*** BEGIN value-header ***/
+GType math_ enum_name@_get_type (void) G_GNUC_CONST;
+#define @ENUMPREFIX _TYPE_@ENUMSHORT@ (math_ enum_name@_get_type ())
+/*** END value-header ***/
+
+/*** BEGIN file-tail ***/
+G_END_DECLS
+
+#endif /* __MATH_ENUMS_H__ */
+/*** END file-tail ***/
diff --git a/src/math-equation.c b/src/math-equation.c
index 010f25d..fc04ddb 100644
--- a/src/math-equation.c
+++ b/src/math-equation.c
@@ -24,14 +24,14 @@
 #include <math.h>
 #include <errno.h>
 #include <glib.h>
-#include <langinfo.h>
-#include <locale.h>
 
 #include "math-equation.h"
 
 #include "mp.h"
 #include "mp-equation.h"
+#include "mp-serializer.h"
 #include "currency.h"
+#include "math-enums.h"
 
 
 enum {
@@ -48,7 +48,8 @@ enum {
     PROP_WORD_SIZE,
     PROP_ANGLE_UNITS,
     PROP_SOURCE_CURRENCY,
-    PROP_TARGET_CURRENCY
+    PROP_TARGET_CURRENCY,
+    PROP_SERIALIZER
 };
 
 static GType number_mode_type, number_format_type, angle_unit_type;
@@ -71,22 +72,14 @@ struct MathEquationPrivate
 {
     GtkTextTag *ans_tag;
 
-    gint show_tsep;           /* Set if the thousands separator should be shown. */
-    gint show_zeroes;         /* Set if trailing zeroes should be shown. */
-    DisplayFormat format;     /* Number display mode. */
-    gint accuracy;            /* Number of digits to show */
     gint word_size;           /* Word size in bits */
     MPAngleUnit angle_units;  /* Units for trigonometric functions */
     char *source_currency;
     char *target_currency;
-    gint base;                /* Numeric base */
     NumberMode number_mode;   /* ??? */
     gboolean can_super_minus; /* TRUE if entering minus can generate a superscript minus */
 
     const char *digits[16];   /* Localized digit values */
-    const char *radix;        /* Locale specific radix string. */
-    const char *tsep;         /* Locale specific thousands separator. */
-    gint tsep_count;          /* Number of digits between separator. */
 
     GtkTextMark *ans_start, *ans_end;
 
@@ -100,6 +93,7 @@ struct MathEquationPrivate
     gboolean in_delete;
 
     MathVariables *variables;
+    MpSerializer *serializer;
 };
 
 G_DEFINE_TYPE (MathEquation, math_equation, GTK_TYPE_TEXT_BUFFER);
@@ -149,7 +143,7 @@ reformat_ans(MathEquation *equation)
     gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &ans_start, equation->priv->ans_start);
     gtk_text_buffer_get_iter_at_mark(GTK_TEXT_BUFFER(equation), &ans_end, equation->priv->ans_end);
     orig_ans_text = gtk_text_buffer_get_text(GTK_TEXT_BUFFER(equation), &ans_start, &ans_end, FALSE);
-    display_make_number(equation, ans_text, MAX_DIGITS, &equation->priv->state.ans);
+    mp_serializer_to_standard_string(equation->priv->serializer, &equation->priv->state.ans, ans_text, MAX_DIGITS);
     if (strcmp(orig_ans_text, ans_text) != 0) {
         gint start;
 
@@ -173,7 +167,7 @@ reformat_ans(MathEquation *equation)
     g_free(orig_ans_text);
 }
 
-
+#if 0
 /* NOTE: Not efficent but easy to write */
 // FIXME: This is just a lexer - use the same lexer as the solver
 static void
@@ -320,7 +314,7 @@ reformat_separators(MathEquation *equation)
     g_free(text);
 #endif
 }
-
+#endif
 
 static void
 reformat_display(MathEquation *equation, gint old_base)
@@ -329,10 +323,10 @@ reformat_display(MathEquation *equation, gint old_base)
     reformat_ans(equation);
 
     /* Add/remove base suffixes if have changed base */
-    reformat_base(equation, old_base);
+    //reformat_base(equation, old_base);
 
     /* Add/remove thousands separators */
-    reformat_separators(equation);
+    //reformat_separators(equation);
 }
 
 
@@ -542,26 +536,14 @@ math_equation_get_digit_text(MathEquation *equation, guint digit)
 }
 
 
-const gchar *
-math_equation_get_numeric_point_text(MathEquation *equation)
-{
-    return equation->priv->radix;
-}
-
-
-const gchar *math_equation_get_thousands_separator_text(MathEquation *equation)
-{
-    return equation->priv->tsep;
-}
-
-
 void
 math_equation_set_accuracy(MathEquation *equation, gint accuracy)
 {
-    if (equation->priv->accuracy == accuracy)
+    gint old_accuracy = mp_serializer_get_accuracy(equation->priv->serializer);
+    if (old_accuracy == accuracy)
         return;
-    equation->priv->accuracy = accuracy;
-    reformat_display(equation, equation->priv->base);
+    mp_serializer_set_accuracy(equation->priv->serializer, accuracy);
+    reformat_display(equation, mp_serializer_get_base(equation->priv->serializer));
     g_object_notify(G_OBJECT(equation), "accuracy");
 }
 
@@ -569,17 +551,18 @@ math_equation_set_accuracy(MathEquation *equation, gint accuracy)
 gint
 math_equation_get_accuracy(MathEquation *equation)
 {
-    return equation->priv->accuracy;
+    return mp_serializer_get_accuracy(equation->priv->serializer);
 }
 
 
 void
 math_equation_set_show_thousands_separators(MathEquation *equation, gboolean visible)
 {
-    if ((equation->priv->show_tsep && visible) || (!equation->priv->show_tsep && !visible))
+    gboolean old_visible = mp_serializer_get_show_thousands_separators(equation->priv->serializer);
+    if (old_visible == visible)
         return;
-    equation->priv->show_tsep = visible;
-    reformat_display(equation, equation->priv->base);
+    mp_serializer_set_show_thousands_separators(equation->priv->serializer, visible);
+    reformat_display(equation, mp_serializer_get_base(equation->priv->serializer));
     g_object_notify(G_OBJECT(equation), "show-thousands-separators");
 }
 
@@ -587,17 +570,18 @@ math_equation_set_show_thousands_separators(MathEquation *equation, gboolean vis
 gboolean
 math_equation_get_show_thousands_separators(MathEquation *equation)
 {
-    return equation->priv->show_tsep;
+    return mp_serializer_get_show_thousands_separators(equation->priv->serializer);
 }
 
 
 void
 math_equation_set_show_trailing_zeroes(MathEquation *equation, gboolean visible)
 {
-    if ((equation->priv->show_zeroes && visible) || (!equation->priv->show_zeroes && !visible))
+    gboolean old_visible = mp_serializer_get_show_trailing_zeroes(equation->priv->serializer);
+    if (old_visible == visible)
         return;
-    equation->priv->show_zeroes = visible;
-    reformat_display(equation, equation->priv->base);
+    mp_serializer_set_show_trailing_zeroes(equation->priv->serializer, visible);
+    reformat_display(equation, mp_serializer_get_base(equation->priv->serializer));
     g_object_notify(G_OBJECT(equation), "show-trailing-zeroes");
 }
 
@@ -605,18 +589,19 @@ math_equation_set_show_trailing_zeroes(MathEquation *equation, gboolean visible)
 gboolean
 math_equation_get_show_trailing_zeroes(MathEquation *equation)
 {
-    return equation->priv->show_zeroes;
+    return mp_serializer_get_show_trailing_zeroes(equation->priv->serializer);
 }
 
 
 void
 math_equation_set_number_format(MathEquation *equation, DisplayFormat format)
 {
-    if (equation->priv->format == format)
+    DisplayFormat old_format = mp_serializer_get_number_format(equation->priv->serializer);
+    if (old_format == format)
         return;
 
-    equation->priv->format = format;
-    reformat_display(equation, equation->priv->base);
+    mp_serializer_set_number_format(equation->priv->serializer, format);
+    reformat_display(equation, mp_serializer_get_base(equation->priv->serializer));
     g_object_notify(G_OBJECT(equation), "number-format");
 }
 
@@ -624,20 +609,19 @@ math_equation_set_number_format(MathEquation *equation, DisplayFormat format)
 DisplayFormat
 math_equation_get_number_format(MathEquation *equation)
 {
-    return equation->priv->format;
+    return mp_serializer_get_number_format(equation->priv->serializer);
 }
 
 
 void
 math_equation_set_base(MathEquation *equation, gint base)
 {
-    gint old_base;
+    gint old_base = mp_serializer_get_base(equation->priv->serializer);
 
-    if (equation->priv->base == base)
+    if (old_base == base)
         return;
 
-    old_base = equation->priv->base;
-    equation->priv->base = base;
+    mp_serializer_set_base(equation->priv->serializer, base);
     reformat_display(equation, old_base);
     g_object_notify(G_OBJECT(equation), "base");
 }
@@ -646,7 +630,7 @@ math_equation_set_base(MathEquation *equation, gint base)
 gint
 math_equation_get_base(MathEquation *equation)
 {
-    return equation->priv->base;
+    return mp_serializer_get_base(equation->priv->serializer);
 }
 
 
@@ -803,12 +787,17 @@ math_equation_get_number(MathEquation *equation, MPNumber *z)
     gboolean result;
 
     text = math_equation_get_display(equation);
-    result = !mp_set_from_string(text, equation->priv->base, z);
+    result = !mp_serializer_from_string(equation->priv->serializer, text, z);
     g_free (text);
 
     return result;
 }
 
+MpSerializer*
+math_equation_get_serializer(MathEquation *equation)
+{
+    return equation->priv->serializer;
+}
 
 void
 math_equation_set_number_mode(MathEquation *equation, NumberMode mode)
@@ -872,7 +861,7 @@ math_equation_set_number(MathEquation *equation, const MPNumber *x)
     GtkTextIter start, end;
 
     /* Show the number in the user chosen format */
-    display_make_number(equation, text, MAX_DIGITS, x);
+    mp_serializer_to_standard_string(equation->priv->serializer, x, text, MAX_DIGITS);
     gtk_text_buffer_set_text(GTK_TEXT_BUFFER(equation), text, -1);
     mp_set_from_mp(x, &equation->priv->state.ans);
 
@@ -936,7 +925,7 @@ math_equation_insert_digit(MathEquation *equation, guint digit)
 void
 math_equation_insert_numeric_point(MathEquation *equation)
 {
-    math_equation_insert(equation, math_equation_get_numeric_point_text(equation));
+    math_equation_insert(equation, mp_serializer_get_numeric_point_text(equation->priv->serializer));
 }
 
 
@@ -944,7 +933,7 @@ void
 math_equation_insert_number(MathEquation *equation, const MPNumber *x)
 {
     char text[MAX_DIGITS];
-    display_make_number(equation, text, MAX_DIGITS, x);
+    mp_serializer_to_standard_string(equation->priv->serializer, x, text, MAX_DIGITS);
     math_equation_insert(equation, text);
 }
 
@@ -1044,7 +1033,7 @@ parse(MathEquation *equation, const char *text, MPNumber *z, char **error_token)
     MPEquationOptions options;
 
     memset(&options, 0, sizeof(options));
-    options.base = equation->priv->base;
+    options.base = mp_serializer_get_base(equation->priv->serializer);
     options.wordlen = equation->priv->word_size;
     options.angle_units = equation->priv->angle_units;
     options.variable_is_defined = variable_is_defined;
@@ -1163,7 +1152,7 @@ math_equation_factorize(MathEquation *equation)
         MPNumber *n;
 
         n = factor->data;
-        display_make_number(equation, temp, MAX_DIGITS, n);
+        mp_serializer_to_standard_string(equation->priv->serializer, n, temp, MAX_DIGITS);
         g_string_append(text, temp);
         if (factor->next)
             g_string_append(text, "Ã?");
@@ -1269,25 +1258,6 @@ math_equation_toggle_bit(MathEquation *equation, guint bit)
 }
 
 
-/* Convert MP number to character string. */
-//FIXME: What to do with this?
-void
-display_make_number(MathEquation *equation, char *target, int target_len, const MPNumber *x)
-{
-    switch(equation->priv->format) {
-    case FIX:
-        mp_cast_to_string(x, equation->priv->base, equation->priv->base, equation->priv->accuracy, !equation->priv->show_zeroes, true, target, target_len);
-        break;
-    case SCI:
-        mp_cast_to_exponential_string(x, equation->priv->base, equation->priv->base, equation->priv->accuracy, !equation->priv->show_zeroes, false, true, target, target_len);
-        break;
-    case ENG:
-        mp_cast_to_exponential_string(x, equation->priv->base, equation->priv->base, equation->priv->accuracy, !equation->priv->show_zeroes, true, true, target, target_len);
-        break;
-    }
-}
-
-
 static void
 math_equation_set_property(GObject      *object,
                            guint         prop_id,
@@ -1371,16 +1341,16 @@ math_equation_get_property(GObject    *object,
         g_value_set_enum(value, self->priv->number_mode);
         break;
     case PROP_ACCURACY:
-        g_value_set_int(value, self->priv->accuracy);
+        g_value_set_int(value, mp_serializer_get_accuracy(self->priv->serializer));
         break;
     case PROP_SHOW_THOUSANDS_SEPARATORS:
-        g_value_set_boolean(value, self->priv->show_tsep);
+        g_value_set_boolean(value, mp_serializer_get_show_thousands_separators(self->priv->serializer));
         break;
     case PROP_SHOW_TRAILING_ZEROES:
-        g_value_set_boolean(value, self->priv->show_zeroes);
+        g_value_set_boolean(value, mp_serializer_get_show_trailing_zeroes(self->priv->serializer));
         break;
     case PROP_NUMBER_FORMAT:
-        g_value_set_enum(value, self->priv->format);
+        g_value_set_enum(value, mp_serializer_get_number_format(self->priv->serializer));
         break;
     case PROP_BASE:
         g_value_set_int(value, math_equation_get_base(self));
@@ -1397,6 +1367,9 @@ math_equation_get_property(GObject    *object,
     case PROP_TARGET_CURRENCY:
         g_value_set_string(value, self->priv->target_currency);
         break;
+    case PROP_SERIALIZER:
+        g_value_set_object(value, self->priv->serializer);
+        break;
     default:
         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
         break;
@@ -1414,13 +1387,6 @@ math_equation_class_init (MathEquationClass *klass)
       {SUBSCRIPT,   "subscript",   "subscript"},
       {0, NULL, NULL}
     };
-    static GEnumValue number_format_values[] =
-    {
-      {FIX, "fixed-point", "fixed-point"},
-      {SCI, "scientific",  "scientific"},
-      {ENG, "engineering", "engineering"},
-      {0, NULL, NULL}
-    };
     static GEnumValue angle_unit_values[] =
     {
       {MP_RADIANS,  "radians",  "radians"},
@@ -1436,7 +1402,7 @@ math_equation_class_init (MathEquationClass *klass)
     g_type_class_add_private (klass, sizeof (MathEquationPrivate));
   
     number_mode_type = g_enum_register_static("NumberMode", number_mode_values);
-    number_format_type = g_enum_register_static("DisplayFormat", number_format_values);
+    number_format_type = math_display_format_get_type();
     angle_unit_type = g_enum_register_static("AngleUnit", angle_unit_values);
 
     g_object_class_install_property(object_class,
@@ -1533,6 +1499,13 @@ math_equation_class_init (MathEquationClass *klass)
                                                         "target Currency",
                                                         "",
                                                         G_PARAM_READWRITE));
+    g_object_class_install_property(object_class,
+                                    PROP_SERIALIZER,
+                                    g_param_spec_object("serializer",
+                                                        "serializer",
+                                                        "Serializer",
+                                                        MP_TYPE_SERIALIZER,
+                                                        G_PARAM_READABLE));
 }
 
 
@@ -1648,7 +1621,6 @@ math_equation_init(MathEquation *equation)
     /* Digits localized for the given language */
     const char *digit_values = _("0,1,2,3,4,5,6,7,8,9,A,B,C,D,E,F");
     const char *default_digits[] = {"0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D", "E", "F"};
-    gchar *radix, *tsep;
     gchar **digits;
     gboolean use_default_digits = FALSE;
     int i;
@@ -1675,28 +1647,15 @@ math_equation_init(MathEquation *equation)
     }
     g_strfreev(digits);
 
-    setlocale(LC_NUMERIC, "");
-
-    radix = nl_langinfo(RADIXCHAR);
-    equation->priv->radix = *radix ? g_locale_to_utf8(radix, -1, NULL, NULL, NULL) : g_strdup(".");
-    tsep = nl_langinfo(THOUSEP);
-    equation->priv->tsep = *tsep ? g_locale_to_utf8(tsep, -1, NULL, NULL, NULL) : g_strdup(",");
-
-    equation->priv->tsep_count = 3;
-  
     equation->priv->variables = math_variables_new();
 
     equation->priv->state.status = g_strdup("");
-    equation->priv->show_zeroes = FALSE;
-    equation->priv->show_tsep = FALSE;
-    equation->priv->format = FIX;
-    equation->priv->accuracy = 9;
     equation->priv->word_size = 32;
     equation->priv->angle_units = MP_DEGREES;
     // FIXME: Pick based on locale
     equation->priv->source_currency = g_strdup(currency_names[0].short_name);
     equation->priv->target_currency = g_strdup(currency_names[0].short_name);
-    equation->priv->base = 10;
+    equation->priv->serializer = mp_serializer_new();
 
     mp_set_from_integer(0, &equation->priv->state.ans);
 }
diff --git a/src/math-equation.h b/src/math-equation.h
index e93137d..93f495c 100644
--- a/src/math-equation.h
+++ b/src/math-equation.h
@@ -24,6 +24,7 @@
 #include <gtk/gtk.h>
 #include "mp.h"
 #include "math-variables.h"
+#include "mp-serializer.h"
 
 G_BEGIN_DECLS
 
@@ -42,13 +43,6 @@ typedef struct
     GtkTextBufferClass parent_class;
 } MathEquationClass;
 
-/* Number display mode. */
-typedef enum {
-  FIX,
-  SCI,
-  ENG
-} DisplayFormat;
-
 typedef enum {
     NORMAL,
     SUPERSCRIPT,
@@ -104,6 +98,7 @@ void math_equation_set_target_currency(MathEquation *equation, const gchar *curr
 const gchar *math_equation_get_target_currency(MathEquation *equation);
 
 const MPNumber *math_equation_get_answer(MathEquation *equation);
+MpSerializer *math_equation_get_serializer(MathEquation *equation);
 
 void math_equation_copy(MathEquation *equation);
 void math_equation_paste(MathEquation *equation);
@@ -127,7 +122,4 @@ void math_equation_clear(MathEquation *equation);
 void math_equation_shift(MathEquation *equation, gint count);
 void math_equation_toggle_bit(MathEquation *equation, guint bit);
 
-//FIXME: Obsolete
-void display_make_number(MathEquation *equation, char *target, int target_len, const MPNumber *x);
-
 #endif /* MATH_EQUATION_H */
diff --git a/src/math-variables.c b/src/math-variables.c
index 53ef1af..58a9815 100644
--- a/src/math-variables.c
+++ b/src/math-variables.c
@@ -20,6 +20,7 @@
 #include <string.h>
 
 #include "math-variables.h"
+#include "mp-serializer.h"
 
 
 struct MathVariablesPrivate
@@ -97,7 +98,7 @@ registers_save(MathVariables *variables)
         MPNumber *value = val;
         char number[1024];
 
-        mp_cast_to_string(value, 10, 10, 50, TRUE, FALSE, number, 1024);
+        mp_serializer_to_specific_string(value, 10, 50, true, false, number, 1024);
         fprintf(f, "%s=%s\n", name, number);
     }
     fclose(f);
diff --git a/src/mp-binary.c b/src/mp-binary.c
index c2a6d16..6c8811c 100644
--- a/src/mp-binary.c
+++ b/src/mp-binary.c
@@ -21,6 +21,7 @@
 
 #include "mp.h"
 #include "mp-private.h"
+#include "mp-serializer.h"
 
 // FIXME: Make dynamic
 #define MAX_DIGITS 1000
@@ -45,8 +46,8 @@ mp_bitwise(const MPNumber *x, const MPNumber *y, int (*bitwise_operator)(int, in
     char text1[MAX_DIGITS], text2[MAX_DIGITS], text_out[MAX_DIGITS], text_out2[MAX_DIGITS];
     int offset1, offset2, offset_out;
 
-    mp_cast_to_string(x, 16, 16, 0, 0, FALSE, text1, MAX_DIGITS);
-    mp_cast_to_string(y, 16, 16, 0, 0, FALSE, text2, MAX_DIGITS);
+    mp_serializer_to_specific_string(x, 16, 0, false, false, text1, MAX_DIGITS);
+    mp_serializer_to_specific_string(y, 16, 0, false, false, text2, MAX_DIGITS);
     offset1 = strlen(text1) - 1;
     offset2 = strlen(text2) - 1;
     offset_out = wordlen / 4 - 1;
@@ -156,7 +157,7 @@ mp_mask(const MPNumber *x, int wordlen, MPNumber *z)
     size_t len, offset;
 
     /* Convert to a hexadecimal string and use last characters */
-    mp_cast_to_string(x, 16, 16, 0, 0, FALSE, text, MAX_DIGITS);
+    mp_serializer_to_specific_string(x, 16, 0, false, false, text, MAX_DIGITS);
     len = strlen(text);
     offset = wordlen / 4;
     offset = len > offset ? len - offset: 0;
diff --git a/src/mp-convert.c b/src/mp-convert.c
index bbdb8fc..8ef633e 100644
--- a/src/mp-convert.c
+++ b/src/mp-convert.c
@@ -507,233 +507,6 @@ mp_cast_to_double(const MPNumber *x)
     }
 }
 
-
-static const char*
-mp_radix_char()
-{
-    static const char* radix = NULL;
-    if (radix == NULL) {
-        radix = nl_langinfo(RADIXCHAR);
-        radix = *radix ? g_locale_to_utf8(radix, -1, NULL, NULL, NULL) : g_strdup(".");
-    }
-    return radix;
-}
-
-
-static void
-mp_cast_to_string_real(const MPNumber *x, int default_base, int base, int accuracy, bool trim_zeroes, bool force_sign, bool localize_radix, GString *string)
-{
-    static char digits[] = "0123456789ABCDEF";
-    MPNumber number, integer_component, fractional_component, temp;
-    int i, last_non_zero;
-
-    if (mp_is_negative(x))
-        mp_abs(x, &number);
-    else
-        mp_set_from_mp(x, &number);
-
-    /* Add rounding factor */
-    mp_set_from_integer(base, &temp);
-    mp_xpowy_integer(&temp, -(accuracy+1), &temp);
-    mp_multiply_integer(&temp, base, &temp);
-    mp_divide_integer(&temp, 2, &temp);
-    mp_add(&number, &temp, &number);
-
-    /* Split into integer and fractional component */
-    mp_floor(&number, &integer_component);
-    mp_fractional_component(&number, &fractional_component);
-
-    /* Write out the integer component least significant digit to most */
-    mp_set_from_mp(&integer_component, &temp);
-    do {
-        MPNumber t, t2, t3;
-        int64_t d;
-
-        mp_divide_integer(&temp, base, &t);
-        mp_floor(&t, &t);
-        mp_multiply_integer(&t, base, &t2);
-
-        mp_subtract(&temp, &t2, &t3);
-
-        d = mp_cast_to_int(&t3);
-        g_string_prepend_c(string, d < 16 ? digits[d] : '?');
-
-        mp_set_from_mp(&t, &temp);
-    } while (!mp_is_zero(&temp));
-
-    last_non_zero = string->len;
-
-    /* Allow user to change locale without breaking saved numbers */
-    if (localize_radix)
-        g_string_append(string, mp_radix_char());
-    else
-        g_string_append_c(string, '.');
-
-    /* Write out the fractional component */
-    mp_set_from_mp(&fractional_component, &temp);
-    for (i = accuracy; i > 0 && !mp_is_zero(&temp); i--) {
-        int d;
-        MPNumber digit;
-
-        mp_multiply_integer(&temp, base, &temp);
-        mp_floor(&temp, &digit);
-        d = mp_cast_to_int(&digit);
-
-        g_string_append_c(string, digits[d]);
-
-        if(d != 0)
-            last_non_zero = string->len;
-        mp_subtract(&temp, &digit, &temp);
-    }
-
-    /* Strip trailing zeroes */
-    if (trim_zeroes || accuracy == 0)
-        g_string_truncate(string, last_non_zero);
-
-    /* Add sign on non-zero values */
-    if (strcmp(string->str, "0") != 0 || force_sign) {
-        if (mp_is_negative(x))
-            g_string_prepend(string, "â??");
-        else if (force_sign)
-            g_string_prepend(string, "+");
-    }
-
-    /* Append base suffix if not in default base */
-    if (base != default_base) {
-        const char *digits[] = {"â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??"};
-        int multiplier = 1;
-        int b = base;
-
-        while (base / multiplier != 0)
-            multiplier *= 10;
-        while (multiplier != 1) {
-            int d;
-            multiplier /= 10;
-            d = b / multiplier;
-            g_string_append(string, digits[d]);
-            b -= d * multiplier;
-        }
-    }
-}
-
-
-void
-mp_cast_to_string(const MPNumber *x, int default_base, int base, int accuracy, bool trim_zeroes, bool localize_radix, char *buffer, int buffer_length)
-{
-    GString *string;
-    MPNumber x_real;
-
-    string = g_string_sized_new(buffer_length);
-
-    mp_real_component(x, &x_real);
-    mp_cast_to_string_real(&x_real, default_base, base, accuracy, trim_zeroes, FALSE, localize_radix, string);
-    if (mp_is_complex(x)) {
-        GString *s;
-        gboolean force_sign = TRUE;
-        MPNumber x_im;
-
-        mp_imaginary_component(x, &x_im);
-
-        if (strcmp(string->str, "0") == 0) {
-            g_string_assign(string, "");
-            force_sign = false;
-        }
-
-        s = g_string_sized_new(buffer_length);
-        mp_cast_to_string_real(&x_im, default_base, 10, accuracy, trim_zeroes, force_sign, localize_radix, s);
-        if (strcmp(s->str, "0") == 0 || strcmp(s->str, "+0") == 0 || strcmp(s->str, "â??0") == 0) {
-            /* Ignore */
-        }
-        else if (strcmp(s->str, "1") == 0) {
-            g_string_append(string, "i");
-        }
-        else if (strcmp(s->str, "+1") == 0) {
-            g_string_append(string, "+i");
-        }
-        else if (strcmp(s->str, "â??1") == 0) {
-            g_string_append(string, "â??i");
-        }
-        else {
-            if (strcmp(s->str, "+0") == 0)
-                g_string_append(string, "+");
-            else if (strcmp(s->str, "0") != 0)
-                g_string_append(string, s->str);
-
-            g_string_append(string, "i");
-        }
-        g_string_free(s, TRUE);
-    }
-
-    // FIXME: Check for truncation
-    strncpy(buffer, string->str, buffer_length);
-    g_string_free(string, TRUE);
-}
-
-
-void
-mp_cast_to_exponential_string(const MPNumber *x, int default_base, int base_, int max_digits, bool trim_zeroes, bool eng_format, bool localize_radix, char *buffer, int buffer_length)
-{
-    char fixed[1024], *c;
-    MPNumber t, z, base, base3, base10, base10inv, mantissa;
-    int exponent = 0;
-    GString *string;
-    const char *super_digits[] = {"�", "¹", "²", "³", "�", "�", "�", "�", "�", "�"};
-
-    string = g_string_sized_new(buffer_length);
-
-    mp_abs(x, &z);
-    if (mp_is_negative(x))
-        g_string_append(string, "â??");
-    mp_set_from_mp(&z, &mantissa);
-
-    mp_set_from_integer(base_, &base);
-    mp_xpowy_integer(&base, 3, &base3);
-    mp_xpowy_integer(&base, 10, &base10);
-    mp_set_from_integer(1, &t);
-    mp_divide(&t, &base10, &base10inv);
-
-    if (!mp_is_zero(&mantissa)) {
-        while (!eng_format && mp_is_greater_equal(&mantissa, &base10)) {
-            exponent += 10;
-            mp_multiply(&mantissa, &base10inv, &mantissa);
-        }
-
-        while ((!eng_format &&  mp_is_greater_equal(&mantissa, &base)) ||
-                (eng_format && (mp_is_greater_equal(&mantissa, &base3) || exponent % 3 != 0))) {
-            exponent += 1;
-            mp_divide(&mantissa, &base, &mantissa);
-        }
-
-        while (!eng_format && mp_is_less_than(&mantissa, &base10inv)) {
-            exponent -= 10;
-            mp_multiply(&mantissa, &base10, &mantissa);
-        }
-
-        mp_set_from_integer(1, &t);
-        while (mp_is_less_than(&mantissa, &t) || (eng_format && exponent % 3 != 0)) {
-            exponent -= 1;
-            mp_multiply(&mantissa, &base, &mantissa);
-        }
-    }
-
-    mp_cast_to_string(&mantissa, default_base, base_, max_digits, trim_zeroes, localize_radix, fixed, 1024);
-    g_string_append(string, fixed);
-    if (exponent != 0) {
-        g_string_append_printf(string, "Ã?10"); // FIXME: Use the current base
-        if (exponent < 0) {
-            exponent = -exponent;
-            g_string_append(string, "â?»");
-        }
-        snprintf(fixed, 1024, "%d", exponent);
-        for (c = fixed; *c; c++)
-            g_string_append(string, super_digits[*c - '0']);
-    }
-
-    strncpy(buffer, string->str, buffer_length);
-    g_string_free(string, TRUE);
-}
-
-
 static int
 char_val(char **c, int base)
 {
@@ -856,10 +629,16 @@ mp_set_from_string(const char *str, int default_base, MPNumber *z)
     int numerators[]            = { 1,   1,   2,   1,   3,   1,   2,   3,   4,   1,   5,   1,   3,   5,   7};
     int denominators[]          = { 2,   3,   3,   4,   4,   5,   5,   5,   5,   6,   6,   8,   8,   8,   8};
     static const char *tsep = NULL;
+    static const char *radix = NULL;
+
+    if (radix == NULL) {
+        radix = nl_langinfo(RADIXCHAR);
+        radix = *radix ? g_locale_to_utf8(radix, -1, NULL, NULL, NULL) : g_strdup(".");
+    }
 
     if (tsep == NULL) {
         tsep = nl_langinfo(THOUSEP);
-        tsep = *tsep ? g_locale_to_utf8(tsep, -1, NULL, NULL, NULL) : g_strdup(",");
+        tsep = *tsep ? g_locale_to_utf8(tsep, -1, NULL, NULL, NULL) : tsep;
     }
 
     if (strstr(str, "°"))
@@ -900,11 +679,6 @@ mp_set_from_string(const char *str, int default_base, MPNumber *z)
     /* Convert integer part */
     mp_set_from_integer(0, z);
     while (1) {
-        if (strncmp(c, tsep, strlen(tsep)) == 0) {
-            c += strlen(tsep);
-            continue;
-        }
-
         i = char_val((char **)&c, base);
         if (i < 0)
             break;
@@ -913,6 +687,11 @@ mp_set_from_string(const char *str, int default_base, MPNumber *z)
             return true;
         mp_multiply_integer(z, base, z);
         mp_add_integer(z, i, z);
+
+        // Must be after the first digit, as locales with tsep = ' ' becomes
+        // buggy otherwise.
+        if (*tsep && strncmp(c, tsep, strlen(tsep)) == 0)
+            c += strlen(tsep);
     }
 
     /* Look for fraction characters, e.g. â?? */
@@ -932,7 +711,7 @@ mp_set_from_string(const char *str, int default_base, MPNumber *z)
      * english '.', since at least europeans frequently use '.' despite ','
      * being the correct one for their locale.
      */
-    if (*c == '.' || strncmp(c, mp_radix_char(), strlen(mp_radix_char())) == 0) {
+    if (*c == '.' || strncmp(c, radix, strlen(radix)) == 0) {
         has_fraction = TRUE;
         c++;
     }
diff --git a/src/mp-equation-lexer.l b/src/mp-equation-lexer.l
index 6318a81..5d55861 100644
--- a/src/mp-equation-lexer.l
+++ b/src/mp-equation-lexer.l
@@ -26,7 +26,6 @@
  */
 
 #include <stdlib.h>
-#include <locale.h>
 #include <string.h>
 #include <sys/types.h>
 
diff --git a/src/mp-serializer.c b/src/mp-serializer.c
new file mode 100644
index 0000000..8d466a0
--- /dev/null
+++ b/src/mp-serializer.c
@@ -0,0 +1,508 @@
+/*  Copyright (c) 2010 Robin Sonefors
+ *
+ *  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, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ */
+
+#include <langinfo.h>
+#include <glib.h>
+#include <glib-object.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "mp-serializer.h"
+
+#include "math-enums.h"
+
+enum {
+    PROP_0,
+    PROP_SHOW_THOUSANDS_SEPARATORS,
+    PROP_SHOW_TRAILING_ZEROES,
+    PROP_NUMBER_FORMAT,
+    PROP_BASE,
+};
+
+static GType number_format_type;
+
+struct MpSerializerPrivate
+{
+    gint accuracy;            /* Number of digits to show */
+    DisplayFormat format;     /* Number display mode. */
+    gint show_tsep;           /* Set if the thousands separator should be shown. */
+    gint show_zeroes;         /* Set if trailing zeroes should be shown. */
+
+    gint base;                /* Numeric base */
+
+    char *tsep;         /* Locale specific thousands separator. */
+    char *radix;        /* Locale specific radix string. */
+    gint tsep_count;          /* Number of digits between separator. */
+};
+
+
+G_DEFINE_TYPE(MpSerializer, mp_serializer, G_TYPE_OBJECT);
+
+MpSerializer *
+mp_serializer_new()
+{
+    return g_object_new(mp_serializer_get_type(), NULL);
+}
+
+static void
+mp_cast_to_string_real(MpSerializer *serializer, const MPNumber *x, int base, bool force_sign, GString *string)
+{
+    static char digits[] = "0123456789ABCDEF";
+    MPNumber number, integer_component, fractional_component, temp;
+    int i, last_non_zero;
+
+    if (mp_is_negative(x))
+        mp_abs(x, &number);
+    else
+        mp_set_from_mp(x, &number);
+
+    /* Add rounding factor */
+    mp_set_from_integer(base, &temp);
+    mp_xpowy_integer(&temp, -(serializer->priv->accuracy+1), &temp);
+    mp_multiply_integer(&temp, base, &temp);
+    mp_divide_integer(&temp, 2, &temp);
+    mp_add(&number, &temp, &number);
+
+    /* Split into integer and fractional component */
+    mp_floor(&number, &integer_component);
+    mp_fractional_component(&number, &fractional_component);
+
+    /* Write out the integer component least significant digit to most */
+    mp_set_from_mp(&integer_component, &temp);
+    i = 0;
+    do {
+        MPNumber t, t2, t3;
+        int64_t d;
+
+        mp_divide_integer(&temp, base, &t);
+        mp_floor(&t, &t);
+        mp_multiply_integer(&t, base, &t2);
+
+        mp_subtract(&temp, &t2, &t3);
+
+        d = mp_cast_to_int(&t3);
+        g_string_prepend_c(string, d < 16 ? digits[d] : '?');
+
+        i++;
+        if (serializer->priv->show_tsep && i == serializer->priv->tsep_count)
+            g_string_prepend(string, serializer->priv->tsep);
+
+        mp_set_from_mp(&t, &temp);
+    } while (!mp_is_zero(&temp));
+
+    last_non_zero = string->len;
+
+    g_string_append(string, serializer->priv->radix);
+
+    /* Write out the fractional component */
+    mp_set_from_mp(&fractional_component, &temp);
+    for (i = serializer->priv->accuracy; i > 0 && !mp_is_zero(&temp); i--) {
+        int d;
+        MPNumber digit;
+
+        mp_multiply_integer(&temp, base, &temp);
+        mp_floor(&temp, &digit);
+        d = mp_cast_to_int(&digit);
+
+        g_string_append_c(string, digits[d]);
+
+        if(d != 0)
+            last_non_zero = string->len;
+        mp_subtract(&temp, &digit, &temp);
+    }
+
+    /* Strip trailing zeroes */
+    if (!serializer->priv->show_zeroes || serializer->priv->accuracy == 0)
+        g_string_truncate(string, last_non_zero);
+
+    /* Add sign on non-zero values */
+    if (strcmp(string->str, "0") != 0 || force_sign) {
+        if (mp_is_negative(x))
+            g_string_prepend(string, "â??");
+        else if (force_sign)
+            g_string_prepend(string, "+");
+    }
+
+    /* Append base suffix if not in default base */
+    if (base != serializer->priv->base) {
+        const char *digits[] = {"â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??", "â??"};
+        int multiplier = 1;
+        int b = base;
+
+        while (base / multiplier != 0)
+            multiplier *= 10;
+        while (multiplier != 1) {
+            int d;
+            multiplier /= 10;
+            d = b / multiplier;
+            g_string_append(string, digits[d]);
+            b -= d * multiplier;
+        }
+    }
+}
+
+
+static void
+mp_cast_to_string(MpSerializer *serializer, const MPNumber *x, char *buffer, int buffer_length)
+{
+    GString *string;
+    MPNumber x_real;
+
+    string = g_string_sized_new(buffer_length);
+
+    mp_real_component(x, &x_real);
+    mp_cast_to_string_real(serializer, &x_real, serializer->priv->base, false, string);
+    if (mp_is_complex(x)) {
+        GString *s;
+        gboolean force_sign = true;
+        MPNumber x_im;
+
+        mp_imaginary_component(x, &x_im);
+
+        if (strcmp(string->str, "0") == 0) {
+            g_string_assign(string, "");
+            force_sign = false;
+        }
+
+        s = g_string_sized_new(buffer_length);
+        mp_cast_to_string_real(serializer, &x_im, 10, force_sign, s);
+        if (strcmp(s->str, "0") == 0 || strcmp(s->str, "+0") == 0 || strcmp(s->str, "â??0") == 0) {
+            /* Ignore */
+        }
+        else if (strcmp(s->str, "1") == 0) {
+            g_string_append(string, "i");
+        }
+        else if (strcmp(s->str, "+1") == 0) {
+            g_string_append(string, "+i");
+        }
+        else if (strcmp(s->str, "â??1") == 0) {
+            g_string_append(string, "â??i");
+        }
+        else {
+            if (strcmp(s->str, "+0") == 0)
+                g_string_append(string, "+");
+            else if (strcmp(s->str, "0") != 0)
+                g_string_append(string, s->str);
+
+            g_string_append(string, "i");
+        }
+        g_string_free(s, TRUE);
+    }
+
+    // FIXME: Check for truncation
+    strncpy(buffer, string->str, buffer_length);
+    g_string_free(string, TRUE);
+}
+
+
+static void
+mp_cast_to_exponential_string(MpSerializer *serializer, const MPNumber *x, bool eng_format, char *buffer, int buffer_length)
+{
+    char fixed[1024], *c;
+    MPNumber t, z, base, base3, base10, base10inv, mantissa;
+    int exponent = 0;
+    GString *string;
+    const char *super_digits[] = {"�", "¹", "²", "³", "�", "�", "�", "�", "�", "�"};
+
+    string = g_string_sized_new(buffer_length);
+
+    mp_abs(x, &z);
+    if (mp_is_negative(x))
+        g_string_append(string, "â??");
+    mp_set_from_mp(&z, &mantissa);
+
+    mp_set_from_integer(serializer->priv->base, &base);
+    mp_xpowy_integer(&base, 3, &base3);
+    mp_xpowy_integer(&base, 10, &base10);
+    mp_set_from_integer(1, &t);
+    mp_divide(&t, &base10, &base10inv);
+
+    if (!mp_is_zero(&mantissa)) {
+        while (!eng_format && mp_is_greater_equal(&mantissa, &base10)) {
+            exponent += 10;
+            mp_multiply(&mantissa, &base10inv, &mantissa);
+        }
+
+        while ((!eng_format &&  mp_is_greater_equal(&mantissa, &base)) ||
+                (eng_format && (mp_is_greater_equal(&mantissa, &base3) || exponent % 3 != 0))) {
+            exponent += 1;
+            mp_divide(&mantissa, &base, &mantissa);
+        }
+
+        while (!eng_format && mp_is_less_than(&mantissa, &base10inv)) {
+            exponent -= 10;
+            mp_multiply(&mantissa, &base10, &mantissa);
+        }
+
+        mp_set_from_integer(1, &t);
+        while (mp_is_less_than(&mantissa, &t) || (eng_format && exponent % 3 != 0)) {
+            exponent -= 1;
+            mp_multiply(&mantissa, &base, &mantissa);
+        }
+    }
+
+    mp_cast_to_string(serializer, &mantissa, fixed, 1024);
+    g_string_append(string, fixed);
+    if (exponent != 0) {
+        g_string_append_printf(string, "Ã?10"); // FIXME: Use the current base
+        if (exponent < 0) {
+            exponent = -exponent;
+            g_string_append(string, "â?»");
+        }
+        snprintf(fixed, 1024, "%d", exponent);
+        for (c = fixed; *c; c++)
+            g_string_append(string, super_digits[*c - '0']);
+    }
+
+    strncpy(buffer, string->str, buffer_length);
+    g_string_free(string, TRUE);
+}
+
+
+void
+mp_serializer_to_standard_string(MpSerializer *serializer, const MPNumber *x, char *target, int target_len)
+{
+    switch(serializer->priv->format) {
+    case FIX:
+        mp_cast_to_string(serializer, x, target, target_len);
+        break;
+    case SCI:
+        mp_cast_to_exponential_string(serializer, x, false, target, target_len);
+        break;
+    case ENG:
+        mp_cast_to_exponential_string(serializer, x, true, target, target_len);
+        break;
+    }
+}
+
+void
+mp_serializer_to_specific_string(const MPNumber *x, int base, int accuracy, bool trim_zeroes, bool localize, char *target, int target_len)
+{
+    MpSerializer *serializer = mp_serializer_new ();
+    if (!localize) {
+        g_free(serializer->priv->radix);
+        serializer->priv->radix = g_strdup(".");
+        serializer->priv->show_tsep = false;
+    }
+    serializer->priv->base = base;
+    serializer->priv->accuracy = accuracy;
+    serializer->priv->show_zeroes = !trim_zeroes;
+    mp_serializer_to_standard_string(serializer, x, target, target_len);
+    g_object_unref(serializer);
+}
+
+bool
+mp_serializer_from_string(MpSerializer *serializer, const char *str, MPNumber *z)
+{
+    return mp_set_from_string(str, serializer->priv->base, z);
+}
+
+void
+mp_serializer_set_base(MpSerializer *serializer, gint base)
+{
+    serializer->priv->base = base;
+}
+
+int
+mp_serializer_get_base(MpSerializer *serializer)
+{
+    return serializer->priv->base;
+}
+
+const gchar *
+mp_serializer_get_numeric_point_text(MpSerializer *serializer)
+{
+    return serializer->priv->radix;
+}
+
+
+const gchar *
+mp_serializer_get_thousands_separator_text(MpSerializer *serializer)
+{
+    return serializer->priv->tsep;
+}
+
+void
+mp_serializer_set_show_thousands_separators(MpSerializer *serializer, gboolean visible)
+{
+    serializer->priv->show_tsep = visible;
+}
+
+
+gboolean
+mp_serializer_get_show_thousands_separators(MpSerializer *serializer)
+{
+    return serializer->priv->show_tsep;
+}
+
+
+void
+mp_serializer_set_show_trailing_zeroes(MpSerializer *serializer, gboolean visible)
+{
+    serializer->priv->show_zeroes = visible;
+}
+
+
+gboolean
+mp_serializer_get_show_trailing_zeroes(MpSerializer *serializer)
+{
+    return serializer->priv->show_zeroes;
+}
+
+
+int
+mp_serializer_get_accuracy(MpSerializer *serializer)
+{
+    return serializer->priv->accuracy;
+}
+
+void
+mp_serializer_set_accuracy(MpSerializer *serializer, int accuracy)
+{
+    serializer->priv->accuracy = accuracy;
+}
+
+DisplayFormat
+mp_serializer_get_number_format(MpSerializer *serializer)
+{
+    return serializer->priv->format;
+}
+
+void
+mp_serializer_set_number_format(MpSerializer *serializer, DisplayFormat format)
+{
+    serializer->priv->format = format;
+}
+
+static void
+mp_serializer_set_property(GObject *object,
+                          guint prop_id,
+                          const GValue *value,
+                          GParamSpec *pspec)
+{
+    MpSerializer *self = MP_SERIALIZER(object);
+
+    switch (prop_id) {
+    case PROP_SHOW_THOUSANDS_SEPARATORS:
+        mp_serializer_set_show_thousands_separators(self, g_value_get_boolean(value));
+        break;
+    case PROP_SHOW_TRAILING_ZEROES:
+        mp_serializer_set_show_trailing_zeroes(self, g_value_get_boolean(value));
+        break;
+    case PROP_BASE:
+        mp_serializer_set_base(self, g_value_get_int(value));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+mp_serializer_get_property(GObject *object,
+                          guint prop_id,
+                          GValue *value,
+                          GParamSpec *pspec)
+{
+    MpSerializer *self = MP_SERIALIZER(object);
+
+    switch (prop_id) {
+    case PROP_SHOW_THOUSANDS_SEPARATORS:
+        g_value_set_boolean(value, mp_serializer_get_show_thousands_separators(self));
+        break;
+    case PROP_SHOW_TRAILING_ZEROES:
+        g_value_set_boolean(value, mp_serializer_get_show_trailing_zeroes(self));
+        break;
+    case PROP_BASE:
+        g_value_set_int(value, mp_serializer_get_base(self));
+        break;
+    default:
+        G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+        break;
+    }
+}
+
+static void
+mp_serializer_finalize (GObject *gobject)
+{
+    MpSerializer *serializer = MP_SERIALIZER(gobject);
+    g_free(serializer->priv->radix);
+    g_free(serializer->priv->tsep);
+}
+
+static void
+mp_serializer_class_init (MpSerializerClass *klass)
+{
+    GObjectClass *object_class = G_OBJECT_CLASS(klass);
+    object_class->get_property = mp_serializer_get_property;
+    object_class->set_property = mp_serializer_set_property;
+    object_class->finalize = mp_serializer_finalize;
+
+    g_type_class_add_private (klass, sizeof(MpSerializerPrivate));
+
+    number_format_type = math_display_format_get_type();
+
+    g_object_class_install_property(object_class,
+                                    PROP_SHOW_THOUSANDS_SEPARATORS,
+                                    g_param_spec_boolean("show-thousands-separators",
+                                                         "show-thousands-separators",
+                                                         "Show thousands separators",
+                                                         TRUE,
+                                                         G_PARAM_READWRITE));
+    g_object_class_install_property(object_class,
+                                    PROP_SHOW_TRAILING_ZEROES,
+                                    g_param_spec_boolean("show-trailing-zeroes",
+                                                         "show-trailing-zeroes",
+                                                         "Show trailing zeroes",
+                                                         FALSE,
+                                                         G_PARAM_READWRITE));
+    g_object_class_install_property(object_class,
+                                    PROP_BASE,
+                                    g_param_spec_int("base",
+                                                     "base",
+                                                     "Default number base (derived from number-format)",
+                                                     2, 16, 10, 
+                                                     G_PARAM_READWRITE));
+    g_object_class_install_property(object_class,
+                                    PROP_NUMBER_FORMAT,
+                                    g_param_spec_enum("number-format",
+                                                      "number-format",
+                                                      "Display format",
+                                                      number_format_type,
+                                                      FIX,
+                                                      G_PARAM_READWRITE));
+}
+
+static void
+mp_serializer_init(MpSerializer *serializer)
+{
+    gchar *radix, *tsep;
+    serializer->priv = G_TYPE_INSTANCE_GET_PRIVATE(serializer, mp_serializer_get_type(), MpSerializerPrivate);
+
+    radix = nl_langinfo(RADIXCHAR);
+    serializer->priv->radix = *radix ? g_locale_to_utf8(radix, -1, NULL, NULL, NULL) : g_strdup(".");
+    tsep = nl_langinfo(THOUSEP);
+    serializer->priv->tsep = *tsep ? g_locale_to_utf8(tsep, -1, NULL, NULL, NULL) : g_strdup("");
+
+    serializer->priv->tsep_count = 3;
+    serializer->priv->base = 10;
+    serializer->priv->accuracy = 9;
+    serializer->priv->show_zeroes = FALSE;
+    serializer->priv->show_tsep = FALSE;
+    serializer->priv->format = FIX;
+}
diff --git a/src/mp-serializer.h b/src/mp-serializer.h
new file mode 100644
index 0000000..3490183
--- /dev/null
+++ b/src/mp-serializer.h
@@ -0,0 +1,72 @@
+/*  Copyright (c) 2010 Robin Sonefors
+ *
+ *  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, or (at your option)
+ *  any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
+ *  02111-1307, USA.
+ */
+
+#ifndef MP_SERIALIZER_H
+#define MP_SERIALIZER_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "mp.h"
+
+G_BEGIN_DECLS
+
+#define MP_TYPE_SERIALIZER (mp_serializer_get_type())
+#define MP_SERIALIZER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), mp_serializer_get_type(), MpSerializer))
+
+typedef struct MpSerializerPrivate MpSerializerPrivate;
+
+typedef struct {
+    GObject parent;
+    MpSerializerPrivate *priv;
+} MpSerializer;
+
+typedef struct {
+    GObjectClass parent;
+} MpSerializerClass;
+
+/* Number display mode. */
+typedef enum {
+  FIX,
+  SCI,
+  ENG
+} DisplayFormat;
+
+GType mp_serializer_get_type(void);
+MpSerializer *mp_serializer_new(void);
+
+void mp_serializer_to_standard_string(MpSerializer *serializer, const MPNumber *z, char *target, int target_len);
+void mp_serializer_to_specific_string(const MPNumber *z, int base, int accuracy, bool trim_zeroes, bool localize, char *target, int target_len);
+bool mp_serializer_from_string(MpSerializer *serializer, const char *str, MPNumber *z);
+
+const gchar* mp_serializer_get_numeric_point_text(MpSerializer *serializer);
+const gchar* mp_serializer_get_thousands_separator_text(MpSerializer *serializer);
+void mp_serializer_set_base(MpSerializer *serializer, int base);
+int mp_serializer_get_base(MpSerializer *serializer);
+gboolean mp_serializer_get_show_trailing_zeroes(MpSerializer *serializer);
+void mp_serializer_set_show_trailing_zeroes(MpSerializer *serializer, gboolean visible);
+gboolean mp_serializer_get_show_thousands_separators(MpSerializer *serializer);
+void mp_serializer_set_show_thousands_separators(MpSerializer *serializer, gboolean visible);
+int mp_serializer_get_accuracy(MpSerializer *serializer);
+void mp_serializer_set_accuracy(MpSerializer *serializer, int accuracy);
+DisplayFormat mp_serializer_get_number_format(MpSerializer *serializer);
+void mp_serializer_set_number_format(MpSerializer *serializer, DisplayFormat format);
+
+G_END_DECLS
+
+#endif /* MP_SERIALIZER_H */
diff --git a/src/mp.h b/src/mp.h
index bca77c9..423aa0a 100644
--- a/src/mp.h
+++ b/src/mp.h
@@ -270,24 +270,6 @@ int64_t mp_cast_to_int(const MPNumber *x);
 /* Returns x as a native unsigned integer */
 uint64_t mp_cast_to_unsigned_int(const MPNumber *x);
 
-/* Converts x to a string representation.
- * The string is written into 'buffer' which is guaranteed to be at least 'buffer_length' octets in size.
- * If not enough space is available the string is truncated.
- * The numbers are written in 'base' (e.g. 10).
- * If 'trim_zeroes' is non-zero then strip off trailing zeroes.
- * Fractional components are truncated at 'max_digits' digits.
- */
-void   mp_cast_to_string(const MPNumber *x, int default_base, int base, int max_digits, bool trim_zeroes, bool localize_radix, char *buffer, int buffer_length);
-
-/* Converts x to a string representation in exponential form.
- * The string is written into 'buffer' which is guaranteed to be at least 'buffer_length' octets in size.
- * If not enough space is available the string is truncated.
- * The numbers are written in 'base' (e.g. 10).
- * If 'trim_zeroes' is non-zero then strip off trailing zeroes.
- * Fractional components are truncated at 'max_digits' digits.
- */
-void   mp_cast_to_exponential_string(const MPNumber *x, int default_base, int base, int max_digits, bool trim_zeroes, bool eng_format, bool localize_radix, char *buffer, int buffer_length);
-
 /* Sets z = sin x */
 void   mp_sin(const MPNumber *x, MPAngleUnit unit, MPNumber *z);
 
diff --git a/src/unittest.c b/src/unittest.c
index 417813f..30de463 100644
--- a/src/unittest.c
+++ b/src/unittest.c
@@ -23,6 +23,7 @@
 
 #include "unittest.h"
 #include "mp-equation.h"
+#include "mp-serializer.h"
 
 static MPEquationOptions options;
 
@@ -83,7 +84,7 @@ test(char *expression, char *expected, int expected_error)
     error = mp_equation_parse(expression, &options, &result, NULL);
 
     if(error == 0) {
-        mp_cast_to_string(&result, options.base, options.base, 9, 1, TRUE, result_str, 1024);
+        mp_serializer_to_specific_string(&result, options.base, 9, true, false, result_str, 1024);
         if(expected_error != 0)
             fail("'%s' -> %s, expected error %s", expression, result_str, error_code_to_string(expected_error));
         else if(strcmp(result_str, expected) != 0)



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]