Add a unit-manager object to track units
[gcalctool.git] / src / mp-serializer.c
blob9d2b052e56026474e99a9b5a66454ab241033af9
1 /* Copyright (c) 2010 Robin Sonefors
3 * This program is free software; you can redistribute it and/or modify
4 * it under the terms of the GNU General Public License as published by
5 * the Free Software Foundation; either version 2, or (at your option)
6 * any later version.
8 * This program is distributed in the hope that it will be useful, but
9 * WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
11 * General Public License for more details.
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
16 * 02111-1307, USA.
19 #include <langinfo.h>
20 #include <glib.h>
21 #include <glib-object.h>
22 #include <string.h>
23 #include <stdio.h>
25 #include "mp-serializer.h"
27 #include "math-enums.h"
29 enum {
30 PROP_0,
31 PROP_SHOW_THOUSANDS_SEPARATORS,
32 PROP_SHOW_TRAILING_ZEROES,
33 PROP_NUMBER_FORMAT,
34 PROP_BASE,
37 static GType number_format_type;
39 struct MpSerializerPrivate
41 gint accuracy; /* Number of digits to show */
42 MpDisplayFormat format; /* Number display mode. */
43 gint show_tsep; /* Set if the thousands separator should be shown. */
44 gint show_zeroes; /* Set if trailing zeroes should be shown. */
46 gint base; /* Numeric base */
48 gunichar tsep; /* Locale specific thousands separator. */
49 gunichar radix; /* Locale specific radix string. */
50 gint tsep_count; /* Number of digits between separator. */
54 G_DEFINE_TYPE(MpSerializer, mp_serializer, G_TYPE_OBJECT);
56 MpSerializer *
57 mp_serializer_new(MpDisplayFormat format, int base, int accuracy)
59 MpSerializer *serializer = g_object_new(mp_serializer_get_type(), /*"number-format", format,*/ NULL);
60 mp_serializer_set_number_format(serializer, format);
61 mp_serializer_set_base(serializer, base);
62 mp_serializer_set_accuracy(serializer, accuracy);
63 return serializer;
67 static void
68 mp_cast_to_string_real(MpSerializer *serializer, const MPNumber *x, int base, gboolean force_sign, GString *string)
70 static gchar digits[] = "0123456789ABCDEF";
71 MPNumber number, integer_component, fractional_component, temp;
72 int i, last_non_zero;
74 if (mp_is_negative(x))
75 mp_abs(x, &number);
76 else
77 mp_set_from_mp(x, &number);
79 /* Add rounding factor */
80 mp_set_from_integer(base, &temp);
81 mp_xpowy_integer(&temp, -(serializer->priv->accuracy+1), &temp);
82 mp_multiply_integer(&temp, base, &temp);
83 mp_divide_integer(&temp, 2, &temp);
84 mp_add(&number, &temp, &temp);
86 /* If trying to add rounding factor causes overflow, don't add it */
87 if (!mp_get_error())
88 mp_set_from_mp(&temp, &number);
90 /* Split into integer and fractional component */
91 mp_floor(&number, &integer_component);
92 mp_fractional_component(&number, &fractional_component);
94 /* Write out the integer component least significant digit to most */
95 mp_set_from_mp(&integer_component, &temp);
96 i = 0;
97 do {
98 MPNumber t, t2, t3;
99 int64_t d;
101 mp_divide_integer(&temp, base, &t);
102 mp_floor(&t, &t);
103 mp_multiply_integer(&t, base, &t2);
105 mp_subtract(&temp, &t2, &t3);
107 d = mp_cast_to_int(&t3);
108 g_string_prepend_c(string, d < 16 ? digits[d] : '?');
110 i++;
111 if (serializer->priv->show_tsep && i == serializer->priv->tsep_count) {
112 g_string_prepend_unichar(string, serializer->priv->tsep);
113 i = 0;
116 mp_set_from_mp(&t, &temp);
117 } while (!mp_is_zero(&temp));
119 last_non_zero = string->len;
121 g_string_append_unichar(string, serializer->priv->radix);
123 /* Write out the fractional component */
124 mp_set_from_mp(&fractional_component, &temp);
125 for (i = serializer->priv->accuracy; i > 0 && !mp_is_zero(&temp); i--) {
126 int d;
127 MPNumber digit;
129 mp_multiply_integer(&temp, base, &temp);
130 mp_floor(&temp, &digit);
131 d = mp_cast_to_int(&digit);
133 g_string_append_c(string, digits[d]);
135 if(d != 0)
136 last_non_zero = string->len;
137 mp_subtract(&temp, &digit, &temp);
140 /* Strip trailing zeroes */
141 if (!serializer->priv->show_zeroes || serializer->priv->accuracy == 0)
142 g_string_truncate(string, last_non_zero);
144 /* Add sign on non-zero values */
145 if (strcmp(string->str, "0") != 0 || force_sign) {
146 if (mp_is_negative(x))
147 g_string_prepend(string, "−");
148 else if (force_sign)
149 g_string_prepend(string, "+");
152 /* Append base suffix if not in default base */
153 if (base != serializer->priv->base) {
154 const gchar *digits[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"};
155 int multiplier = 1;
156 int b = base;
158 while (base / multiplier != 0)
159 multiplier *= 10;
160 while (multiplier != 1) {
161 int d;
162 multiplier /= 10;
163 d = b / multiplier;
164 g_string_append(string, digits[d]);
165 b -= d * multiplier;
171 static gchar *
172 mp_cast_to_string(MpSerializer *serializer, const MPNumber *x)
174 GString *string;
175 MPNumber x_real;
176 gchar *result;
178 string = g_string_sized_new(1024);
180 mp_real_component(x, &x_real);
181 mp_cast_to_string_real(serializer, &x_real, serializer->priv->base, FALSE, string);
182 if (mp_is_complex(x)) {
183 GString *s;
184 gboolean force_sign = TRUE;
185 MPNumber x_im;
187 mp_imaginary_component(x, &x_im);
189 if (strcmp(string->str, "0") == 0) {
190 g_string_assign(string, "");
191 force_sign = FALSE;
194 s = g_string_sized_new(1024);
195 mp_cast_to_string_real(serializer, &x_im, 10, force_sign, s);
196 if (strcmp(s->str, "0") == 0 || strcmp(s->str, "+0") == 0 || strcmp(s->str, "−0") == 0) {
197 /* Ignore */
199 else if (strcmp(s->str, "1") == 0) {
200 g_string_append(string, "i");
202 else if (strcmp(s->str, "+1") == 0) {
203 g_string_append(string, "+i");
205 else if (strcmp(s->str, "−1") == 0) {
206 g_string_append(string, "−i");
208 else {
209 if (strcmp(s->str, "+0") == 0)
210 g_string_append(string, "+");
211 else if (strcmp(s->str, "0") != 0)
212 g_string_append(string, s->str);
214 g_string_append(string, "i");
216 g_string_free(s, TRUE);
219 result = g_strndup(string->str, string->len + 1);
220 g_string_free(string, TRUE);
222 return result;
226 static gchar *
227 mp_cast_to_exponential_string(MpSerializer *serializer, const MPNumber *x, gboolean eng_format)
229 gchar *fixed, *c;
230 MPNumber t, z, base, base3, base10, base10inv, mantissa;
231 int exponent = 0;
232 GString *string;
233 gchar *result;
234 const gchar *super_digits[] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"};
236 string = g_string_sized_new(1024);
238 mp_abs(x, &z);
239 if (mp_is_negative(x))
240 g_string_append(string, "−");
241 mp_set_from_mp(&z, &mantissa);
243 mp_set_from_integer(serializer->priv->base, &base);
244 mp_xpowy_integer(&base, 3, &base3);
245 mp_xpowy_integer(&base, 10, &base10);
246 mp_set_from_integer(1, &t);
247 mp_divide(&t, &base10, &base10inv);
249 if (!mp_is_zero(&mantissa)) {
250 while (!eng_format && mp_is_greater_equal(&mantissa, &base10)) {
251 exponent += 10;
252 mp_multiply(&mantissa, &base10inv, &mantissa);
255 while ((!eng_format && mp_is_greater_equal(&mantissa, &base)) ||
256 (eng_format && (mp_is_greater_equal(&mantissa, &base3) || exponent % 3 != 0))) {
257 exponent += 1;
258 mp_divide(&mantissa, &base, &mantissa);
261 while (!eng_format && mp_is_less_than(&mantissa, &base10inv)) {
262 exponent -= 10;
263 mp_multiply(&mantissa, &base10, &mantissa);
266 mp_set_from_integer(1, &t);
267 while (mp_is_less_than(&mantissa, &t) || (eng_format && exponent % 3 != 0)) {
268 exponent -= 1;
269 mp_multiply(&mantissa, &base, &mantissa);
273 fixed = mp_cast_to_string(serializer, &mantissa);
274 g_string_append(string, fixed);
275 g_free(fixed);
276 if (exponent != 0) {
277 gchar *super_value;
279 g_string_append_printf(string, "×10"); // FIXME: Use the current base
280 if (exponent < 0) {
281 exponent = -exponent;
282 g_string_append(string, "⁻");
285 super_value = g_strdup_printf("%d", exponent);
286 for (c = super_value; *c; c++)
287 g_string_append(string, super_digits[*c - '0']);
288 g_free (super_value);
291 result = g_strndup(string->str, string->len + 1);
292 g_string_free(string, TRUE);
294 return result;
298 gchar *
299 mp_serializer_to_string(MpSerializer *serializer, const MPNumber *x)
301 gchar *s0, *s1;
302 switch(serializer->priv->format) {
303 default:
304 case MP_DISPLAY_FORMAT_AUTOMATIC:
305 s0 = mp_cast_to_string(serializer, x);
306 s1 = mp_cast_to_exponential_string(serializer, x, FALSE);
307 if (g_utf8_strlen(s0, -1) < g_utf8_strlen(s1, -1))
309 g_free(s1);
310 return s0;
312 else
314 g_free(s0);
315 return s1;
317 break;
318 case MP_DISPLAY_FORMAT_FIXED:
319 return mp_cast_to_string(serializer, x);
320 case MP_DISPLAY_FORMAT_SCIENTIFIC:
321 return mp_cast_to_exponential_string(serializer, x, FALSE);
322 case MP_DISPLAY_FORMAT_ENGINEERING:
323 return mp_cast_to_exponential_string(serializer, x, TRUE);
328 gboolean
329 mp_serializer_from_string(MpSerializer *serializer, const gchar *str, MPNumber *z)
331 return mp_set_from_string(str, serializer->priv->base, z);
335 void
336 mp_serializer_set_base(MpSerializer *serializer, gint base)
338 serializer->priv->base = base;
343 mp_serializer_get_base(MpSerializer *serializer)
345 return serializer->priv->base;
349 void
350 mp_serializer_set_radix(MpSerializer *serializer, gunichar radix)
352 serializer->priv->radix = radix;
356 gunichar
357 mp_serializer_get_radix(MpSerializer *serializer)
359 return serializer->priv->radix;
363 void
364 mp_serializer_set_thousands_separator(MpSerializer *serializer, gunichar separator)
366 serializer->priv->tsep = separator;
370 gunichar
371 mp_serializer_get_thousands_separator(MpSerializer *serializer)
373 return serializer->priv->tsep;
377 gint
378 mp_serializer_get_thousands_separator_count(MpSerializer *serializer)
380 return serializer->priv->tsep_count;
384 void
385 mp_serializer_set_show_thousands_separators(MpSerializer *serializer, gboolean visible)
387 serializer->priv->show_tsep = visible;
391 gboolean
392 mp_serializer_get_show_thousands_separators(MpSerializer *serializer)
394 return serializer->priv->show_tsep;
398 void
399 mp_serializer_set_show_trailing_zeroes(MpSerializer *serializer, gboolean visible)
401 serializer->priv->show_zeroes = visible;
405 gboolean
406 mp_serializer_get_show_trailing_zeroes(MpSerializer *serializer)
408 return serializer->priv->show_zeroes;
413 mp_serializer_get_accuracy(MpSerializer *serializer)
415 return serializer->priv->accuracy;
419 void
420 mp_serializer_set_accuracy(MpSerializer *serializer, int accuracy)
422 serializer->priv->accuracy = accuracy;
426 MpDisplayFormat
427 mp_serializer_get_number_format(MpSerializer *serializer)
429 return serializer->priv->format;
433 void
434 mp_serializer_set_number_format(MpSerializer *serializer, MpDisplayFormat format)
436 serializer->priv->format = format;
440 static void
441 mp_serializer_set_property(GObject *object,
442 guint prop_id,
443 const GValue *value,
444 GParamSpec *pspec)
446 MpSerializer *self = MP_SERIALIZER(object);
448 switch (prop_id) {
449 case PROP_SHOW_THOUSANDS_SEPARATORS:
450 mp_serializer_set_show_thousands_separators(self, g_value_get_boolean(value));
451 break;
452 case PROP_SHOW_TRAILING_ZEROES:
453 mp_serializer_set_show_trailing_zeroes(self, g_value_get_boolean(value));
454 break;
455 case PROP_BASE:
456 mp_serializer_set_base(self, g_value_get_int(value));
457 break;
458 default:
459 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
460 break;
465 static void
466 mp_serializer_get_property(GObject *object,
467 guint prop_id,
468 GValue *value,
469 GParamSpec *pspec)
471 MpSerializer *self = MP_SERIALIZER(object);
473 switch (prop_id) {
474 case PROP_SHOW_THOUSANDS_SEPARATORS:
475 g_value_set_boolean(value, mp_serializer_get_show_thousands_separators(self));
476 break;
477 case PROP_SHOW_TRAILING_ZEROES:
478 g_value_set_boolean(value, mp_serializer_get_show_trailing_zeroes(self));
479 break;
480 case PROP_BASE:
481 g_value_set_int(value, mp_serializer_get_base(self));
482 break;
483 default:
484 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
485 break;
490 static void
491 mp_serializer_class_init(MpSerializerClass *klass)
493 GObjectClass *object_class = G_OBJECT_CLASS(klass);
494 object_class->get_property = mp_serializer_get_property;
495 object_class->set_property = mp_serializer_set_property;
497 g_type_class_add_private(klass, sizeof(MpSerializerPrivate));
499 number_format_type = math_mp_display_format_get_type();
501 g_object_class_install_property(object_class,
502 PROP_SHOW_THOUSANDS_SEPARATORS,
503 g_param_spec_boolean("show-thousands-separators",
504 "show-thousands-separators",
505 "Show thousands separators",
506 TRUE,
507 G_PARAM_READWRITE));
508 g_object_class_install_property(object_class,
509 PROP_SHOW_TRAILING_ZEROES,
510 g_param_spec_boolean("show-trailing-zeroes",
511 "show-trailing-zeroes",
512 "Show trailing zeroes",
513 FALSE,
514 G_PARAM_READWRITE));
515 g_object_class_install_property(object_class,
516 PROP_NUMBER_FORMAT,
517 g_param_spec_enum("number-format",
518 "number-format",
519 "Display format",
520 number_format_type,
521 MP_DISPLAY_FORMAT_AUTOMATIC,
522 G_PARAM_READWRITE));
523 g_object_class_install_property(object_class,
524 PROP_BASE,
525 g_param_spec_int("base",
526 "base",
527 "Default number base",
528 2, 16, 10,
529 G_PARAM_READWRITE));
533 static void
534 mp_serializer_init(MpSerializer *serializer)
536 gchar *radix, *tsep;
537 serializer->priv = G_TYPE_INSTANCE_GET_PRIVATE(serializer, mp_serializer_get_type(), MpSerializerPrivate);
539 radix = nl_langinfo(RADIXCHAR);
540 serializer->priv->radix = radix ? g_utf8_get_char(g_locale_to_utf8(radix, -1, NULL, NULL, NULL)) : '.';
541 tsep = nl_langinfo(THOUSEP);
542 if (tsep && tsep[0] != '\0')
543 serializer->priv->tsep = g_utf8_get_char(g_locale_to_utf8(tsep, -1, NULL, NULL, NULL));
544 else
545 serializer->priv->tsep = ' ';
546 serializer->priv->tsep_count = 3;
548 serializer->priv->base = 10;
549 serializer->priv->accuracy = 9;
550 serializer->priv->show_zeroes = FALSE;
551 serializer->priv->show_tsep = FALSE;
552 serializer->priv->format = MP_DISPLAY_FORMAT_AUTOMATIC;