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