1 /*
2  * Copyright (C) 2010 Robin Sonefors
3  * Copyright (C) 2008-2011 Robert Ancell.
4  *
5  * This program is free software: you can redistribute it and/or modify it under
6  * the terms of the GNU General Public License as published by the Free Software
7  * Foundation, either version 2 of the License, or (at your option) any later
8  * version. See http://www.gnu.org/copyleft/gpl.html the full text of the
9  * license.
10  */
11 
12 #include <langinfo.h>
13 #include <glib.h>
14 #include <glib-object.h>
15 #include <string.h>
16 #include <stdio.h>
17 
18 #include "mp-serializer.h"
19 #include "mp-enums.h"
20 
21 enum {
22     PROP_0,
23     PROP_SHOW_THOUSANDS_SEPARATORS,
24     PROP_SHOW_TRAILING_ZEROES,
25     PROP_NUMBER_FORMAT,
26     PROP_BASE,
27 };
28 
29 struct MpSerializerPrivate
30 {
31     gint leading_digits;      /* Number of digits to show before radix */
32     gint trailing_digits;     /* Number of digits to show after radix */
33     MpDisplayFormat format;   /* Number display mode. */
34     gint show_tsep;           /* Set if the thousands separator should be shown. */
35     gint show_zeroes;         /* Set if trailing zeroes should be shown. */
36 
37     gint base;                /* Numeric base */
38 
39     gunichar tsep;            /* Locale specific thousands separator. */
40     gunichar radix;           /* Locale specific radix string. */
41     gint tsep_count;          /* Number of digits between separator. */
42 };
43 
44 
45 G_DEFINE_TYPE_WITH_PRIVATE (MpSerializer, mp_serializer, G_TYPE_OBJECT);
46 
47 MpSerializer *
mp_serializer_new(MpDisplayFormat format,int base,int trailing_digits)48 mp_serializer_new(MpDisplayFormat format, int base, int trailing_digits)
49 {
50     MpSerializer *serializer = g_object_new(mp_serializer_get_type(), /*"number-format", format,*/ NULL);
51     mp_serializer_set_number_format(serializer, format);
52     mp_serializer_set_base(serializer, base);
53     mp_serializer_set_trailing_digits(serializer, trailing_digits);
54     return serializer;
55 }
56 
57 
58 static void
mp_to_string_real(MpSerializer * serializer,const MPNumber * x,int base,gboolean force_sign,int * n_digits,GString * string)59 mp_to_string_real(MpSerializer *serializer, const MPNumber *x, int base, gboolean force_sign, int *n_digits, GString *string)
60 {
61     static gchar digits[] = "0123456789ABCDEF";
62     MPNumber number = mp_new();
63     MPNumber integer_component = mp_new();
64     MPNumber fractional_component = mp_new();
65     MPNumber temp = mp_new();
66     int i;
67     gsize last_non_zero;
68 
69     if (mp_is_negative(x))
70         mp_abs(x, &number);
71     else
72         mp_set_from_mp(x, &number);
73 
74     /* Add rounding factor */
75     mp_set_from_integer(base, &temp);
76     mp_xpowy_integer(&temp, -(serializer->priv->trailing_digits+1), &temp);
77     mp_multiply_integer(&temp, base, &temp);
78     mp_divide_integer(&temp, 2, &temp);
79     mp_add(&number, &temp, &temp);
80 
81     /* If trying to add rounding factor causes overflow, don't add it */
82     if (!mp_get_error())
83         mp_set_from_mp(&temp, &number);
84 
85     /* Split into integer and fractional component */
86     mp_floor(&number, &integer_component);
87     mp_fractional_component(&number, &fractional_component);
88 
89     /* Write out the integer component least significant digit to most */
90     mp_set_from_mp(&integer_component, &temp);
91     i = 0;
92     MPNumber t = mp_new();
93     MPNumber t2 = mp_new();
94     MPNumber t3 = mp_new();
95     do {
96         long d;
97 
98         if (serializer->priv->base == 10 && serializer->priv->show_tsep && i == serializer->priv->tsep_count) {
99             g_string_prepend_unichar(string, serializer->priv->tsep);
100             i = 0;
101         }
102         i++;
103 
104         mp_divide_integer(&temp, base, &t);
105         mp_floor(&t, &t);
106         mp_multiply_integer(&t, base, &t2);
107 
108         mp_subtract(&temp, &t2, &t3);
109 
110         d = mp_to_integer(&t3);
111         if (d < 16 && d >= 0)
112             g_string_prepend_c(string, digits[d]);
113         else
114         {   /* FIXME: Should show an overflow error message in fixed mode */
115             g_string_assign(string, "0");
116             *n_digits = serializer->priv->leading_digits+1;
117             break;
118         }
119         (*n_digits)++;
120 
121         mp_set_from_mp(&t, &temp);
122     } while (!mp_is_zero(&temp));
123     mp_clear(&t);
124     mp_clear(&t2);
125     mp_clear(&t3);
126 
127     last_non_zero = string->len;
128 
129     g_string_append_unichar(string, serializer->priv->radix);
130 
131     /* Write out the fractional component */
132     mp_set_from_mp(&fractional_component, &temp);
133     MPNumber digit = mp_new();
134     for (i = serializer->priv->trailing_digits; i > 0 && !mp_is_zero(&temp); i--) {
135         int d;
136 
137         mp_multiply_integer(&temp, base, &temp);
138         mp_floor(&temp, &digit);
139         d = mp_to_integer(&digit);
140 
141         g_string_append_c(string, digits[d]);
142 
143         if(d != 0)
144             last_non_zero = string->len;
145         mp_subtract(&temp, &digit, &temp);
146     }
147     mp_clear(&digit);
148 
149     /* Strip trailing zeroes */
150     if (!serializer->priv->show_zeroes || serializer->priv->trailing_digits == 0)
151         g_string_truncate(string, last_non_zero);
152 
153     /* Add sign on non-zero values */
154     if (strcmp(string->str, "0") != 0 || force_sign) {
155         if (mp_is_negative(x))
156             g_string_prepend(string, "−");
157         else if (force_sign)
158             g_string_prepend(string, "+");
159     }
160 
161     /* Append base suffix if not in default base */
162     if (base != serializer->priv->base) {
163         const gchar *base_digits[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"};
164         int multiplier = 1;
165         int b = base;
166 
167         while (base / multiplier != 0)
168             multiplier *= 10;
169         while (multiplier != 1) {
170             int d;
171             multiplier /= 10;
172             d = b / multiplier;
173             g_string_append(string, base_digits[d]);
174             b -= d * multiplier;
175         }
176     }
177     mp_clear(&number);
178     mp_clear(&integer_component);
179     mp_clear(&fractional_component);
180     mp_clear(&temp);
181 }
182 
183 
184 static gchar *
mp_to_string(MpSerializer * serializer,const MPNumber * x,int * n_digits)185 mp_to_string(MpSerializer *serializer, const MPNumber *x, int *n_digits)
186 {
187     GString *string;
188     MPNumber x_real = mp_new();
189     gchar *result;
190 
191     string = g_string_sized_new(1024);
192 
193     mp_real_component(x, &x_real);
194     mp_to_string_real(serializer, &x_real, serializer->priv->base, FALSE, n_digits, string);
195     if (mp_is_complex(x)) {
196         GString *s;
197         gboolean force_sign = TRUE;
198         MPNumber x_im = mp_new();
199         int n_complex_digits = 0;
200 
201         mp_imaginary_component(x, &x_im);
202 
203         if (strcmp(string->str, "0") == 0) {
204             g_string_assign(string, "");
205             force_sign = FALSE;
206         }
207 
208         s = g_string_sized_new(1024);
209         mp_to_string_real(serializer, &x_im, 10, force_sign, &n_complex_digits, s);
210         if (n_complex_digits > *n_digits)
211             *n_digits = n_complex_digits;
212         if (strcmp(s->str, "0") == 0 || strcmp(s->str, "+0") == 0 || strcmp(s->str, "−0") == 0) {
213             /* Ignore unless the real part was also zero and string was cleared*/
214             if (string->len == 0) {
215                 g_string_append(string, "0");
216             }
217         }
218         else if (strcmp(s->str, "1") == 0) {
219             g_string_append(string, "i");
220         }
221         else if (strcmp(s->str, "+1") == 0) {
222             g_string_append(string, "+i");
223         }
224         else if (strcmp(s->str, "−1") == 0) {
225             g_string_append(string, "−i");
226         }
227         else {
228             if (strcmp(s->str, "+0") == 0)
229                 g_string_append(string, "+");
230             else if (strcmp(s->str, "0") != 0)
231                 g_string_append(string, s->str);
232 
233             g_string_append(string, "i");
234         }
235         g_string_free(s, TRUE);
236         mp_clear(&x_im);
237     }
238 
239     result = g_strndup(string->str, string->len + 1);
240     g_string_free(string, TRUE);
241     mp_clear(&x_real);
242 
243     return result;
244 }
245 
246 
247 static int
mp_to_exponential_string_real(MpSerializer * serializer,const MPNumber * x,GString * string,gboolean eng_format,int * n_digits)248 mp_to_exponential_string_real(MpSerializer *serializer, const MPNumber *x, GString *string, gboolean eng_format, int *n_digits)
249 {
250     gchar *fixed;
251     MPNumber t = mp_new();
252     MPNumber z = mp_new();
253     MPNumber base = mp_new();
254     MPNumber base3 = mp_new();
255     MPNumber base10 = mp_new();
256     MPNumber base10inv = mp_new();
257     MPNumber mantissa = mp_new();
258     int exponent = 0;
259 
260     mp_abs(x, &z);
261     if (mp_is_negative(x))
262         g_string_append(string, "−");
263     mp_set_from_mp(&z, &mantissa);
264 
265     mp_set_from_integer(serializer->priv->base, &base);
266     mp_xpowy_integer(&base, 3, &base3);
267     mp_xpowy_integer(&base, 10, &base10);
268     mp_set_from_integer(1, &t);
269     mp_divide(&t, &base10, &base10inv);
270 
271     if (!mp_is_zero(&mantissa)) {
272         while (!eng_format && mp_is_greater_equal(&mantissa, &base10)) {
273             exponent += 10;
274             mp_multiply(&mantissa, &base10inv, &mantissa);
275         }
276 
277         while ((!eng_format &&  mp_is_greater_equal(&mantissa, &base)) ||
278                 (eng_format && (mp_is_greater_equal(&mantissa, &base3) || exponent % 3 != 0))) {
279             exponent += 1;
280             mp_divide(&mantissa, &base, &mantissa);
281         }
282 
283         while (!eng_format && mp_is_less_than(&mantissa, &base10inv)) {
284             exponent -= 10;
285             mp_multiply(&mantissa, &base10, &mantissa);
286         }
287 
288         mp_set_from_integer(1, &t);
289         while (mp_is_less_than(&mantissa, &t) || (eng_format && exponent % 3 != 0)) {
290             exponent -= 1;
291             mp_multiply(&mantissa, &base, &mantissa);
292         }
293     }
294 
295     fixed = mp_to_string(serializer, &mantissa, n_digits);
296     g_string_append(string, fixed);
297     g_free(fixed);
298 
299     mp_clear(&t);
300     mp_clear(&z);
301     mp_clear(&base);
302     mp_clear(&base3);
303     mp_clear(&base10);
304     mp_clear(&base10inv);
305     mp_clear(&mantissa);
306 
307     return exponent;
308 }
309 
310 
311 static void
append_exponent(GString * string,int exponent)312 append_exponent(GString *string, int exponent)
313 {
314     const gchar *super_digits[] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"};
315     gchar *super_value, *c;
316 
317     if (exponent == 0)
318         return;
319 
320     g_string_append_printf(string, "×10"); // FIXME: Use the current base
321     if (exponent < 0) {
322         exponent = -exponent;
323         g_string_append(string, "⁻");
324     }
325 
326     super_value = g_strdup_printf("%d", exponent);
327     for (c = super_value; *c; c++)
328         g_string_append(string, super_digits[*c - '0']);
329     g_free (super_value);
330 }
331 
332 
333 static gchar *
mp_to_exponential_string(MpSerializer * serializer,const MPNumber * x,gboolean eng_format,int * n_digits)334 mp_to_exponential_string(MpSerializer *serializer, const MPNumber *x, gboolean eng_format, int *n_digits)
335 {
336     GString *string;
337     MPNumber x_real = mp_new();
338     gchar *result;
339     int exponent;
340 
341     string = g_string_sized_new(1024);
342 
343     mp_real_component(x, &x_real);
344     exponent = mp_to_exponential_string_real(serializer, &x_real, string, eng_format, n_digits);
345     append_exponent(string, exponent);
346 
347     if (mp_is_complex(x)) {
348         GString *s;
349         MPNumber x_im = mp_new();
350         int n_complex_digits = 0;
351 
352         mp_imaginary_component(x, &x_im);
353 
354         if (strcmp(string->str, "0") == 0)
355             g_string_assign(string, "");
356         else if (!mp_is_negative(&x_im))
357             g_string_append(string, "+");
358 
359         s = g_string_sized_new(1024);
360         exponent = mp_to_exponential_string_real(serializer, &x_im, s, eng_format, &n_complex_digits);
361         if (n_complex_digits > *n_digits)
362             *n_digits = n_complex_digits;
363         if (strcmp(s->str, "0") == 0 || strcmp(s->str, "+0") == 0 || strcmp(s->str, "−0") == 0) {
364             /* Ignore */
365         }
366         else if (strcmp(s->str, "1") == 0) {
367             g_string_append(string, "i");
368         }
369         else if (strcmp(s->str, "+1") == 0) {
370             g_string_append(string, "+i");
371         }
372         else if (strcmp(s->str, "−1") == 0) {
373             g_string_append(string, "−i");
374         }
375         else {
376             if (strcmp(s->str, "+0") == 0)
377                 g_string_append(string, "+");
378             else if (strcmp(s->str, "0") != 0)
379                 g_string_append(string, s->str);
380 
381             g_string_append(string, "i");
382         }
383         g_string_free(s, TRUE);
384         mp_clear(&x_im);
385         append_exponent(string, exponent);
386     }
387 
388     result = g_strndup(string->str, string->len + 1);
389     g_string_free(string, TRUE);
390     mp_clear(&x_real);
391 
392     return result;
393 }
394 
395 
396 gchar *
mp_serializer_to_string(MpSerializer * serializer,const MPNumber * x)397 mp_serializer_to_string(MpSerializer *serializer, const MPNumber *x)
398 {
399     gchar *s0;
400     int n_digits = 0;
401     switch(serializer->priv->format) {
402     default:
403     case MP_DISPLAY_FORMAT_AUTOMATIC:
404         s0 = mp_to_string(serializer, x, &n_digits);
405         if (n_digits <= serializer->priv->leading_digits)
406             return s0;
407         else {
408             g_free (s0);
409             return mp_to_exponential_string(serializer, x, FALSE, &n_digits);
410         }
411         break;
412     case MP_DISPLAY_FORMAT_FIXED:
413         return mp_to_string(serializer, x, &n_digits);
414     case MP_DISPLAY_FORMAT_SCIENTIFIC:
415         return mp_to_exponential_string(serializer, x, FALSE, &n_digits);
416     case MP_DISPLAY_FORMAT_ENGINEERING:
417         return mp_to_exponential_string(serializer, x, TRUE, &n_digits);
418     }
419 }
420 
421 
422 gboolean
mp_serializer_from_string(MpSerializer * serializer,const gchar * str,MPNumber * z)423 mp_serializer_from_string(MpSerializer *serializer, const gchar *str, MPNumber *z)
424 {
425     return mp_set_from_string(str, serializer->priv->base, z);
426 }
427 
428 
429 void
mp_serializer_set_base(MpSerializer * serializer,gint base)430 mp_serializer_set_base(MpSerializer *serializer, gint base)
431 {
432     serializer->priv->base = base;
433 }
434 
435 
436 int
mp_serializer_get_base(MpSerializer * serializer)437 mp_serializer_get_base(MpSerializer *serializer)
438 {
439     return serializer->priv->base;
440 }
441 
442 
443 void
mp_serializer_set_radix(MpSerializer * serializer,gunichar radix)444 mp_serializer_set_radix(MpSerializer *serializer, gunichar radix)
445 {
446     serializer->priv->radix = radix;
447 }
448 
449 
450 gunichar
mp_serializer_get_radix(MpSerializer * serializer)451 mp_serializer_get_radix(MpSerializer *serializer)
452 {
453     return serializer->priv->radix;
454 }
455 
456 
457 void
mp_serializer_set_thousands_separator(MpSerializer * serializer,gunichar separator)458 mp_serializer_set_thousands_separator(MpSerializer *serializer, gunichar separator)
459 {
460     serializer->priv->tsep = separator;
461 }
462 
463 
464 gunichar
mp_serializer_get_thousands_separator(MpSerializer * serializer)465 mp_serializer_get_thousands_separator(MpSerializer *serializer)
466 {
467     return serializer->priv->tsep;
468 }
469 
470 
471 gint
mp_serializer_get_thousands_separator_count(MpSerializer * serializer)472 mp_serializer_get_thousands_separator_count(MpSerializer *serializer)
473 {
474     return serializer->priv->tsep_count;
475 }
476 
477 
478 void
mp_serializer_set_show_thousands_separators(MpSerializer * serializer,gboolean visible)479 mp_serializer_set_show_thousands_separators(MpSerializer *serializer, gboolean visible)
480 {
481     serializer->priv->show_tsep = visible;
482 }
483 
484 
485 gboolean
mp_serializer_get_show_thousands_separators(MpSerializer * serializer)486 mp_serializer_get_show_thousands_separators(MpSerializer *serializer)
487 {
488     return serializer->priv->show_tsep;
489 }
490 
491 
492 void
mp_serializer_set_show_trailing_zeroes(MpSerializer * serializer,gboolean visible)493 mp_serializer_set_show_trailing_zeroes(MpSerializer *serializer, gboolean visible)
494 {
495     serializer->priv->show_zeroes = visible;
496 }
497 
498 
499 gboolean
mp_serializer_get_show_trailing_zeroes(MpSerializer * serializer)500 mp_serializer_get_show_trailing_zeroes(MpSerializer *serializer)
501 {
502     return serializer->priv->show_zeroes;
503 }
504 
505 
506 int
mp_serializer_get_leading_digits(MpSerializer * serializer)507 mp_serializer_get_leading_digits(MpSerializer *serializer)
508 {
509     return serializer->priv->leading_digits;
510 }
511 
512 
513 void
mp_serializer_set_leading_digits(MpSerializer * serializer,int leading_digits)514 mp_serializer_set_leading_digits(MpSerializer *serializer, int leading_digits)
515 {
516     serializer->priv->leading_digits = leading_digits;
517 }
518 
519 
520 int
mp_serializer_get_trailing_digits(MpSerializer * serializer)521 mp_serializer_get_trailing_digits(MpSerializer *serializer)
522 {
523     return serializer->priv->trailing_digits;
524 }
525 
526 
527 void
mp_serializer_set_trailing_digits(MpSerializer * serializer,int trailing_digits)528 mp_serializer_set_trailing_digits(MpSerializer *serializer, int trailing_digits)
529 {
530     serializer->priv->trailing_digits = trailing_digits;
531 }
532 
533 
534 MpDisplayFormat
mp_serializer_get_number_format(MpSerializer * serializer)535 mp_serializer_get_number_format(MpSerializer *serializer)
536 {
537     return serializer->priv->format;
538 }
539 
540 
541 void
mp_serializer_set_number_format(MpSerializer * serializer,MpDisplayFormat format)542 mp_serializer_set_number_format(MpSerializer *serializer, MpDisplayFormat format)
543 {
544     serializer->priv->format = format;
545 }
546 
547 
548 static void
mp_serializer_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)549 mp_serializer_set_property(GObject *object,
550                            guint prop_id,
551                            const GValue *value,
552                            GParamSpec *pspec)
553 {
554     MpSerializer *self = MP_SERIALIZER(object);
555 
556     switch (prop_id) {
557     case PROP_SHOW_THOUSANDS_SEPARATORS:
558         mp_serializer_set_show_thousands_separators(self, g_value_get_boolean(value));
559         break;
560     case PROP_SHOW_TRAILING_ZEROES:
561         mp_serializer_set_show_trailing_zeroes(self, g_value_get_boolean(value));
562         break;
563     case PROP_BASE:
564         mp_serializer_set_base(self, g_value_get_int(value));
565         break;
566     default:
567         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
568         break;
569     }
570 }
571 
572 
573 static void
mp_serializer_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)574 mp_serializer_get_property(GObject *object,
575                            guint prop_id,
576                            GValue *value,
577                            GParamSpec *pspec)
578 {
579     MpSerializer *self = MP_SERIALIZER(object);
580 
581     switch (prop_id) {
582     case PROP_SHOW_THOUSANDS_SEPARATORS:
583         g_value_set_boolean(value, mp_serializer_get_show_thousands_separators(self));
584         break;
585     case PROP_SHOW_TRAILING_ZEROES:
586         g_value_set_boolean(value, mp_serializer_get_show_trailing_zeroes(self));
587         break;
588     case PROP_BASE:
589         g_value_set_int(value, mp_serializer_get_base(self));
590         break;
591     default:
592         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
593         break;
594     }
595 }
596 
597 
598 static void
mp_serializer_class_init(MpSerializerClass * klass)599 mp_serializer_class_init(MpSerializerClass *klass)
600 {
601     GObjectClass *object_class = G_OBJECT_CLASS(klass);
602     object_class->get_property = mp_serializer_get_property;
603     object_class->set_property = mp_serializer_set_property;
604 
605     g_object_class_install_property(object_class,
606                                     PROP_SHOW_THOUSANDS_SEPARATORS,
607                                     g_param_spec_boolean("show-thousands-separators",
608                                                          "show-thousands-separators",
609                                                          "Show thousands separators",
610                                                          TRUE,
611                                                          G_PARAM_READWRITE));
612     g_object_class_install_property(object_class,
613                                     PROP_SHOW_TRAILING_ZEROES,
614                                     g_param_spec_boolean("show-trailing-zeroes",
615                                                          "show-trailing-zeroes",
616                                                          "Show trailing zeroes",
617                                                          FALSE,
618                                                          G_PARAM_READWRITE));
619     g_object_class_install_property(object_class,
620                                     PROP_NUMBER_FORMAT,
621                                     g_param_spec_enum("number-format",
622                                                       "number-format",
623                                                       "Display format",
624                                                       math_mp_display_format_get_type(),
625                                                       MP_DISPLAY_FORMAT_AUTOMATIC,
626                                                       G_PARAM_READWRITE));
627     g_object_class_install_property(object_class,
628                                     PROP_BASE,
629                                     g_param_spec_int("base",
630                                                      "base",
631                                                      "Default number base",
632                                                      2, 16, 10,
633                                                      G_PARAM_READWRITE));
634 }
635 
636 
637 static void
mp_serializer_init(MpSerializer * serializer)638 mp_serializer_init(MpSerializer *serializer)
639 {
640     gchar *radix, *utf8_radix, *tsep;
641     serializer->priv = mp_serializer_get_instance_private (serializer);
642 
643     radix = nl_langinfo(RADIXCHAR);
644     utf8_radix = g_locale_to_utf8(radix, -1, NULL, NULL, NULL);
645     serializer->priv->radix = radix ? g_utf8_get_char(utf8_radix) : '.';
646     g_free(utf8_radix);
647     tsep = nl_langinfo(THOUSEP);
648     if (tsep && tsep[0] != '\0')
649     {
650         gchar *utf8_tsep = g_locale_to_utf8(tsep, -1, NULL, NULL, NULL);
651         serializer->priv->tsep = g_utf8_get_char(utf8_tsep);
652         g_free(utf8_tsep);
653     }
654     else
655         serializer->priv->tsep = ' ';
656     serializer->priv->tsep_count = 3;
657 
658     serializer->priv->base = 10;
659     serializer->priv->leading_digits = 12;
660     serializer->priv->trailing_digits = 9;
661     serializer->priv->show_zeroes = FALSE;
662     serializer->priv->show_tsep = FALSE;
663     serializer->priv->format = MP_DISPLAY_FORMAT_AUTOMATIC;
664 }
665