/* * Copyright (C) 2010 Robin Sonefors * Copyright (C) 2008-2011 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. */ #include #include #include #include #include #include "mp-serializer.h" #include "mp-enums.h" enum { PROP_0, PROP_SHOW_THOUSANDS_SEPARATORS, PROP_SHOW_TRAILING_ZEROES, PROP_NUMBER_FORMAT, PROP_BASE, }; struct MpSerializerPrivate { gint leading_digits; /* Number of digits to show before radix */ gint trailing_digits; /* Number of digits to show after radix */ MpDisplayFormat 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 */ gunichar tsep; /* Locale specific thousands separator. */ gunichar radix; /* Locale specific radix string. */ gint tsep_count; /* Number of digits between separator. */ }; G_DEFINE_TYPE_WITH_PRIVATE (MpSerializer, mp_serializer, G_TYPE_OBJECT); MpSerializer * mp_serializer_new(MpDisplayFormat format, int base, int trailing_digits) { MpSerializer *serializer = g_object_new(mp_serializer_get_type(), /*"number-format", format,*/ NULL); mp_serializer_set_number_format(serializer, format); mp_serializer_set_base(serializer, base); mp_serializer_set_trailing_digits(serializer, trailing_digits); return serializer; } static void mp_to_string_real(MpSerializer *serializer, const MPNumber *x, int base, gboolean force_sign, int *n_digits, GString *string) { static gchar digits[] = "0123456789ABCDEF"; MPNumber number = mp_new(); MPNumber integer_component = mp_new(); MPNumber fractional_component = mp_new(); MPNumber temp = mp_new(); int i; gsize 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->trailing_digits+1), &temp); mp_multiply_integer(&temp, base, &temp); mp_divide_integer(&temp, 2, &temp); mp_add(&number, &temp, &temp); /* If trying to add rounding factor causes overflow, don't add it */ if (!mp_get_error()) mp_set_from_mp(&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 = mp_new(); MPNumber t2 = mp_new(); MPNumber t3 = mp_new(); long d; if (serializer->priv->base == 10 && serializer->priv->show_tsep && i == serializer->priv->tsep_count) { g_string_prepend_unichar(string, serializer->priv->tsep); i = 0; } i++; mp_divide_integer(&temp, base, &t); mp_floor(&t, &t); mp_multiply_integer(&t, base, &t2); mp_subtract(&temp, &t2, &t3); d = mp_to_integer(&t3); if (d < 16 && d >= 0) g_string_prepend_c(string, digits[d]); else { /* FIXME: Should show an overflow error message in fixed mode */ g_string_assign(string, "0"); *n_digits = serializer->priv->leading_digits+1; break; } (*n_digits)++; mp_set_from_mp(&t, &temp); mp_clear(&t); mp_clear(&t2); mp_clear(&t3); } while (!mp_is_zero(&temp)); last_non_zero = string->len; g_string_append_unichar(string, serializer->priv->radix); /* Write out the fractional component */ mp_set_from_mp(&fractional_component, &temp); for (i = serializer->priv->trailing_digits; i > 0 && !mp_is_zero(&temp); i--) { int d; MPNumber digit = mp_new(); mp_multiply_integer(&temp, base, &temp); mp_floor(&temp, &digit); d = mp_to_integer(&digit); g_string_append_c(string, digits[d]); if(d != 0) last_non_zero = string->len; mp_subtract(&temp, &digit, &temp); mp_clear(&digit); } /* Strip trailing zeroes */ if (!serializer->priv->show_zeroes || serializer->priv->trailing_digits == 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 gchar *base_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, base_digits[d]); b -= d * multiplier; } } mp_clear(&number); mp_clear(&integer_component); mp_clear(&fractional_component); mp_clear(&temp); } static gchar * mp_to_string(MpSerializer *serializer, const MPNumber *x, int *n_digits) { GString *string; MPNumber x_real = mp_new(); gchar *result; string = g_string_sized_new(1024); mp_real_component(x, &x_real); mp_to_string_real(serializer, &x_real, serializer->priv->base, FALSE, n_digits, string); if (mp_is_complex(x)) { GString *s; gboolean force_sign = TRUE; MPNumber x_im = mp_new(); int n_complex_digits = 0; mp_imaginary_component(x, &x_im); if (strcmp(string->str, "0") == 0) { g_string_assign(string, ""); force_sign = FALSE; } s = g_string_sized_new(1024); mp_to_string_real(serializer, &x_im, 10, force_sign, &n_complex_digits, s); if (n_complex_digits > *n_digits) *n_digits = n_complex_digits; if (strcmp(s->str, "0") == 0 || strcmp(s->str, "+0") == 0 || strcmp(s->str, "−0") == 0) { /* Ignore unless the real part was also zero and string was cleared*/ if (string->len == 0) { g_string_append(string, "0"); } } 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); mp_clear(&x_im); } result = g_strndup(string->str, string->len + 1); g_string_free(string, TRUE); mp_clear(&x_real); return result; } static int mp_to_exponential_string_real(MpSerializer *serializer, const MPNumber *x, GString *string, gboolean eng_format, int *n_digits) { gchar *fixed; MPNumber t = mp_new(); MPNumber z = mp_new(); MPNumber base = mp_new(); MPNumber base3 = mp_new(); MPNumber base10 = mp_new(); MPNumber base10inv = mp_new(); MPNumber mantissa = mp_new(); int exponent = 0; 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); } } fixed = mp_to_string(serializer, &mantissa, n_digits); g_string_append(string, fixed); g_free(fixed); mp_clear(&t); mp_clear(&z); mp_clear(&base); mp_clear(&base3); mp_clear(&base10); mp_clear(&base10inv); mp_clear(&mantissa); return exponent; } static void append_exponent(GString *string, int exponent) { const gchar *super_digits[] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"}; gchar *super_value, *c; if (exponent == 0) return; g_string_append_printf(string, "×10"); // FIXME: Use the current base if (exponent < 0) { exponent = -exponent; g_string_append(string, "⁻"); } super_value = g_strdup_printf("%d", exponent); for (c = super_value; *c; c++) g_string_append(string, super_digits[*c - '0']); g_free (super_value); } static gchar * mp_to_exponential_string(MpSerializer *serializer, const MPNumber *x, gboolean eng_format, int *n_digits) { GString *string; MPNumber x_real = mp_new(); gchar *result; int exponent; string = g_string_sized_new(1024); mp_real_component(x, &x_real); exponent = mp_to_exponential_string_real(serializer, &x_real, string, eng_format, n_digits); append_exponent(string, exponent); if (mp_is_complex(x)) { GString *s; MPNumber x_im = mp_new(); int n_complex_digits = 0; mp_imaginary_component(x, &x_im); if (strcmp(string->str, "0") == 0) g_string_assign(string, ""); else if (!mp_is_negative(&x_im)) g_string_append(string, "+"); s = g_string_sized_new(1024); exponent = mp_to_exponential_string_real(serializer, &x_im, s, eng_format, &n_complex_digits); if (n_complex_digits > *n_digits) *n_digits = n_complex_digits; 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); mp_clear(&x_im); append_exponent(string, exponent); } result = g_strndup(string->str, string->len + 1); g_string_free(string, TRUE); mp_clear(&x_real); return result; } gchar * mp_serializer_to_string(MpSerializer *serializer, const MPNumber *x) { gchar *s0; int n_digits = 0; switch(serializer->priv->format) { default: case MP_DISPLAY_FORMAT_AUTOMATIC: s0 = mp_to_string(serializer, x, &n_digits); if (n_digits <= serializer->priv->leading_digits) return s0; else { g_free (s0); return mp_to_exponential_string(serializer, x, FALSE, &n_digits); } break; case MP_DISPLAY_FORMAT_FIXED: return mp_to_string(serializer, x, &n_digits); case MP_DISPLAY_FORMAT_SCIENTIFIC: return mp_to_exponential_string(serializer, x, FALSE, &n_digits); case MP_DISPLAY_FORMAT_ENGINEERING: return mp_to_exponential_string(serializer, x, TRUE, &n_digits); } } gboolean mp_serializer_from_string(MpSerializer *serializer, const gchar *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; } void mp_serializer_set_radix(MpSerializer *serializer, gunichar radix) { serializer->priv->radix = radix; } gunichar mp_serializer_get_radix(MpSerializer *serializer) { return serializer->priv->radix; } void mp_serializer_set_thousands_separator(MpSerializer *serializer, gunichar separator) { serializer->priv->tsep = separator; } gunichar mp_serializer_get_thousands_separator(MpSerializer *serializer) { return serializer->priv->tsep; } gint mp_serializer_get_thousands_separator_count(MpSerializer *serializer) { return serializer->priv->tsep_count; } 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_leading_digits(MpSerializer *serializer) { return serializer->priv->leading_digits; } void mp_serializer_set_leading_digits(MpSerializer *serializer, int leading_digits) { serializer->priv->leading_digits = leading_digits; } int mp_serializer_get_trailing_digits(MpSerializer *serializer) { return serializer->priv->trailing_digits; } void mp_serializer_set_trailing_digits(MpSerializer *serializer, int trailing_digits) { serializer->priv->trailing_digits = trailing_digits; } MpDisplayFormat mp_serializer_get_number_format(MpSerializer *serializer) { return serializer->priv->format; } void mp_serializer_set_number_format(MpSerializer *serializer, MpDisplayFormat 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_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; 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_NUMBER_FORMAT, g_param_spec_enum("number-format", "number-format", "Display format", math_mp_display_format_get_type(), MP_DISPLAY_FORMAT_AUTOMATIC, G_PARAM_READWRITE)); g_object_class_install_property(object_class, PROP_BASE, g_param_spec_int("base", "base", "Default number base", 2, 16, 10, G_PARAM_READWRITE)); } static void mp_serializer_init(MpSerializer *serializer) { gchar *radix, *tsep; serializer->priv = mp_serializer_get_instance_private (serializer); radix = nl_langinfo(RADIXCHAR); serializer->priv->radix = radix ? g_utf8_get_char(g_locale_to_utf8(radix, -1, NULL, NULL, NULL)) : '.'; tsep = nl_langinfo(THOUSEP); if (tsep && tsep[0] != '\0') serializer->priv->tsep = g_utf8_get_char(g_locale_to_utf8(tsep, -1, NULL, NULL, NULL)); else serializer->priv->tsep = ' '; serializer->priv->tsep_count = 3; serializer->priv->base = 10; serializer->priv->leading_digits = 12; serializer->priv->trailing_digits = 9; serializer->priv->show_zeroes = FALSE; serializer->priv->show_tsep = FALSE; serializer->priv->format = MP_DISPLAY_FORMAT_AUTOMATIC; }