Updated Latvian translation and added Latvian translation for help by Viesturs Ružāns...
[gcalctool.git] / src / mp-serializer.c
blob356fc55181e819d9e08beea732efd033a87e481a
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.
12 #include <langinfo.h>
13 #include <glib.h>
14 #include <glib-object.h>
15 #include <string.h>
16 #include <stdio.h>
18 #include "mp-serializer.h"
19 #include "mp-enums.h"
21 enum {
22 PROP_0,
23 PROP_SHOW_THOUSANDS_SEPARATORS,
24 PROP_SHOW_TRAILING_ZEROES,
25 PROP_NUMBER_FORMAT,
26 PROP_BASE,
29 struct MpSerializerPrivate
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. */
37 gint base; /* Numeric base */
39 gunichar tsep; /* Locale specific thousands separator. */
40 gunichar radix; /* Locale specific radix string. */
41 gint tsep_count; /* Number of digits between separator. */
45 G_DEFINE_TYPE(MpSerializer, mp_serializer, G_TYPE_OBJECT);
47 MpSerializer *
48 mp_serializer_new(MpDisplayFormat format, int base, int trailing_digits)
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;
58 static void
59 mp_cast_to_string_real(MpSerializer *serializer, const MPNumber *x, int base, gboolean force_sign, int *n_digits, GString *string)
61 static gchar digits[] = "0123456789ABCDEF";
62 MPNumber number, integer_component, fractional_component, temp;
63 int i, last_non_zero;
65 if (mp_is_negative(x))
66 mp_abs(x, &number);
67 else
68 mp_set_from_mp(x, &number);
70 /* Add rounding factor */
71 mp_set_from_integer(base, &temp);
72 mp_xpowy_integer(&temp, -(serializer->priv->trailing_digits+1), &temp);
73 mp_multiply_integer(&temp, base, &temp);
74 mp_divide_integer(&temp, 2, &temp);
75 mp_add(&number, &temp, &temp);
77 /* If trying to add rounding factor causes overflow, don't add it */
78 if (!mp_get_error())
79 mp_set_from_mp(&temp, &number);
81 /* Split into integer and fractional component */
82 mp_floor(&number, &integer_component);
83 mp_fractional_component(&number, &fractional_component);
85 /* Write out the integer component least significant digit to most */
86 mp_set_from_mp(&integer_component, &temp);
87 i = 0;
88 do {
89 MPNumber t, t2, t3;
90 int64_t d;
92 if (serializer->priv->base == 10 && serializer->priv->show_tsep && i == serializer->priv->tsep_count) {
93 g_string_prepend_unichar(string, serializer->priv->tsep);
94 i = 0;
96 i++;
98 mp_divide_integer(&temp, base, &t);
99 mp_floor(&t, &t);
100 mp_multiply_integer(&t, base, &t2);
102 mp_subtract(&temp, &t2, &t3);
104 d = mp_cast_to_int(&t3);
105 g_string_prepend_c(string, d < 16 ? digits[d] : '?');
106 (*n_digits)++;
108 mp_set_from_mp(&t, &temp);
109 } while (!mp_is_zero(&temp));
111 last_non_zero = string->len;
113 g_string_append_unichar(string, serializer->priv->radix);
115 /* Write out the fractional component */
116 mp_set_from_mp(&fractional_component, &temp);
117 for (i = serializer->priv->trailing_digits; i > 0 && !mp_is_zero(&temp); i--) {
118 int d;
119 MPNumber digit;
121 mp_multiply_integer(&temp, base, &temp);
122 mp_floor(&temp, &digit);
123 d = mp_cast_to_int(&digit);
125 g_string_append_c(string, digits[d]);
127 if(d != 0)
128 last_non_zero = string->len;
129 mp_subtract(&temp, &digit, &temp);
132 /* Strip trailing zeroes */
133 if (!serializer->priv->show_zeroes || serializer->priv->trailing_digits == 0)
134 g_string_truncate(string, last_non_zero);
136 /* Add sign on non-zero values */
137 if (strcmp(string->str, "0") != 0 || force_sign) {
138 if (mp_is_negative(x))
139 g_string_prepend(string, "−");
140 else if (force_sign)
141 g_string_prepend(string, "+");
144 /* Append base suffix if not in default base */
145 if (base != serializer->priv->base) {
146 const gchar *digits[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"};
147 int multiplier = 1;
148 int b = base;
150 while (base / multiplier != 0)
151 multiplier *= 10;
152 while (multiplier != 1) {
153 int d;
154 multiplier /= 10;
155 d = b / multiplier;
156 g_string_append(string, digits[d]);
157 b -= d * multiplier;
163 static gchar *
164 mp_cast_to_string(MpSerializer *serializer, const MPNumber *x, int *n_digits)
166 GString *string;
167 MPNumber x_real;
168 gchar *result;
170 string = g_string_sized_new(1024);
172 mp_real_component(x, &x_real);
173 mp_cast_to_string_real(serializer, &x_real, serializer->priv->base, FALSE, n_digits, string);
174 if (mp_is_complex(x)) {
175 GString *s;
176 gboolean force_sign = TRUE;
177 MPNumber x_im;
178 int n_complex_digits;
180 mp_imaginary_component(x, &x_im);
182 if (strcmp(string->str, "0") == 0) {
183 g_string_assign(string, "");
184 force_sign = FALSE;
187 s = g_string_sized_new(1024);
188 mp_cast_to_string_real(serializer, &x_im, 10, force_sign, &n_complex_digits, s);
189 if (n_complex_digits > *n_digits)
190 *n_digits = n_complex_digits;
191 if (strcmp(s->str, "0") == 0 || strcmp(s->str, "+0") == 0 || strcmp(s->str, "−0") == 0) {
192 /* Ignore */
194 else if (strcmp(s->str, "1") == 0) {
195 g_string_append(string, "i");
197 else if (strcmp(s->str, "+1") == 0) {
198 g_string_append(string, "+i");
200 else if (strcmp(s->str, "−1") == 0) {
201 g_string_append(string, "−i");
203 else {
204 if (strcmp(s->str, "+0") == 0)
205 g_string_append(string, "+");
206 else if (strcmp(s->str, "0") != 0)
207 g_string_append(string, s->str);
209 g_string_append(string, "i");
211 g_string_free(s, TRUE);
214 result = g_strndup(string->str, string->len + 1);
215 g_string_free(string, TRUE);
217 return result;
221 static int
222 mp_cast_to_exponential_string_real(MpSerializer *serializer, const MPNumber *x, GString *string, gboolean eng_format, int *n_digits)
224 gchar *fixed;
225 MPNumber t, z, base, base3, base10, base10inv, mantissa;
226 int exponent = 0;
228 mp_abs(x, &z);
229 if (mp_is_negative(x))
230 g_string_append(string, "−");
231 mp_set_from_mp(&z, &mantissa);
233 mp_set_from_integer(serializer->priv->base, &base);
234 mp_xpowy_integer(&base, 3, &base3);
235 mp_xpowy_integer(&base, 10, &base10);
236 mp_set_from_integer(1, &t);
237 mp_divide(&t, &base10, &base10inv);
239 if (!mp_is_zero(&mantissa)) {
240 while (!eng_format && mp_is_greater_equal(&mantissa, &base10)) {
241 exponent += 10;
242 mp_multiply(&mantissa, &base10inv, &mantissa);
245 while ((!eng_format && mp_is_greater_equal(&mantissa, &base)) ||
246 (eng_format && (mp_is_greater_equal(&mantissa, &base3) || exponent % 3 != 0))) {
247 exponent += 1;
248 mp_divide(&mantissa, &base, &mantissa);
251 while (!eng_format && mp_is_less_than(&mantissa, &base10inv)) {
252 exponent -= 10;
253 mp_multiply(&mantissa, &base10, &mantissa);
256 mp_set_from_integer(1, &t);
257 while (mp_is_less_than(&mantissa, &t) || (eng_format && exponent % 3 != 0)) {
258 exponent -= 1;
259 mp_multiply(&mantissa, &base, &mantissa);
263 fixed = mp_cast_to_string(serializer, &mantissa, n_digits);
264 g_string_append(string, fixed);
265 g_free(fixed);
267 return exponent;
271 static void
272 append_exponent(GString *string, int exponent)
274 const gchar *super_digits[] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"};
275 gchar *super_value, *c;
277 if (exponent == 0)
278 return;
280 g_string_append_printf(string, "×10"); // FIXME: Use the current base
281 if (exponent < 0) {
282 exponent = -exponent;
283 g_string_append(string, "⁻");
286 super_value = g_strdup_printf("%d", exponent);
287 for (c = super_value; *c; c++)
288 g_string_append(string, super_digits[*c - '0']);
289 g_free (super_value);
293 static gchar *
294 mp_cast_to_exponential_string(MpSerializer *serializer, const MPNumber *x, gboolean eng_format, int *n_digits)
296 GString *string;
297 MPNumber x_real;
298 gchar *result;
299 int exponent;
301 string = g_string_sized_new(1024);
303 mp_real_component(x, &x_real);
304 exponent = mp_cast_to_exponential_string_real(serializer, &x_real, string, eng_format, n_digits);
305 append_exponent(string, exponent);
307 if (mp_is_complex(x)) {
308 GString *s;
309 MPNumber x_im;
310 int n_complex_digits = 0;
312 mp_imaginary_component(x, &x_im);
314 if (strcmp(string->str, "0") == 0)
315 g_string_assign(string, "");
317 s = g_string_sized_new(1024);
318 exponent = mp_cast_to_exponential_string_real(serializer, &x_im, s, eng_format, &n_complex_digits);
319 if (n_complex_digits > *n_digits)
320 *n_digits = n_complex_digits;
321 if (strcmp(s->str, "0") == 0 || strcmp(s->str, "+0") == 0 || strcmp(s->str, "−0") == 0) {
322 /* Ignore */
324 else if (strcmp(s->str, "1") == 0) {
325 g_string_append(string, "i");
327 else if (strcmp(s->str, "+1") == 0) {
328 g_string_append(string, "+i");
330 else if (strcmp(s->str, "−1") == 0) {
331 g_string_append(string, "−i");
333 else {
334 if (strcmp(s->str, "+0") == 0)
335 g_string_append(string, "+");
336 else if (strcmp(s->str, "0") != 0)
337 g_string_append(string, s->str);
339 g_string_append(string, "i");
341 g_string_free(s, TRUE);
342 append_exponent(string, exponent);
345 result = g_strndup(string->str, string->len + 1);
346 g_string_free(string, TRUE);
348 return result;
352 gchar *
353 mp_serializer_to_string(MpSerializer *serializer, const MPNumber *x)
355 gchar *s0;
356 int n_digits = 0;
358 switch(serializer->priv->format) {
359 default:
360 case MP_DISPLAY_FORMAT_AUTOMATIC:
361 s0 = mp_cast_to_string(serializer, x, &n_digits);
362 if (n_digits <= serializer->priv->leading_digits)
363 return s0;
364 else {
365 g_free (s0);
366 return mp_cast_to_exponential_string(serializer, x, FALSE, &n_digits);
368 break;
369 case MP_DISPLAY_FORMAT_FIXED:
370 return mp_cast_to_string(serializer, x, &n_digits);
371 case MP_DISPLAY_FORMAT_SCIENTIFIC:
372 return mp_cast_to_exponential_string(serializer, x, FALSE, &n_digits);
373 case MP_DISPLAY_FORMAT_ENGINEERING:
374 return mp_cast_to_exponential_string(serializer, x, TRUE, &n_digits);
379 gboolean
380 mp_serializer_from_string(MpSerializer *serializer, const gchar *str, MPNumber *z)
382 return mp_set_from_string(str, serializer->priv->base, z);
386 void
387 mp_serializer_set_base(MpSerializer *serializer, gint base)
389 serializer->priv->base = base;
394 mp_serializer_get_base(MpSerializer *serializer)
396 return serializer->priv->base;
400 void
401 mp_serializer_set_radix(MpSerializer *serializer, gunichar radix)
403 serializer->priv->radix = radix;
407 gunichar
408 mp_serializer_get_radix(MpSerializer *serializer)
410 return serializer->priv->radix;
414 void
415 mp_serializer_set_thousands_separator(MpSerializer *serializer, gunichar separator)
417 serializer->priv->tsep = separator;
421 gunichar
422 mp_serializer_get_thousands_separator(MpSerializer *serializer)
424 return serializer->priv->tsep;
428 gint
429 mp_serializer_get_thousands_separator_count(MpSerializer *serializer)
431 return serializer->priv->tsep_count;
435 void
436 mp_serializer_set_show_thousands_separators(MpSerializer *serializer, gboolean visible)
438 serializer->priv->show_tsep = visible;
442 gboolean
443 mp_serializer_get_show_thousands_separators(MpSerializer *serializer)
445 return serializer->priv->show_tsep;
449 void
450 mp_serializer_set_show_trailing_zeroes(MpSerializer *serializer, gboolean visible)
452 serializer->priv->show_zeroes = visible;
456 gboolean
457 mp_serializer_get_show_trailing_zeroes(MpSerializer *serializer)
459 return serializer->priv->show_zeroes;
464 mp_serializer_get_leading_digits(MpSerializer *serializer)
466 return serializer->priv->leading_digits;
470 void
471 mp_serializer_set_leading_digits(MpSerializer *serializer, int leading_digits)
473 serializer->priv->leading_digits = leading_digits;
478 mp_serializer_get_trailing_digits(MpSerializer *serializer)
480 return serializer->priv->trailing_digits;
484 void
485 mp_serializer_set_trailing_digits(MpSerializer *serializer, int trailing_digits)
487 serializer->priv->trailing_digits = trailing_digits;
491 MpDisplayFormat
492 mp_serializer_get_number_format(MpSerializer *serializer)
494 return serializer->priv->format;
498 void
499 mp_serializer_set_number_format(MpSerializer *serializer, MpDisplayFormat format)
501 serializer->priv->format = format;
505 static void
506 mp_serializer_set_property(GObject *object,
507 guint prop_id,
508 const GValue *value,
509 GParamSpec *pspec)
511 MpSerializer *self = MP_SERIALIZER(object);
513 switch (prop_id) {
514 case PROP_SHOW_THOUSANDS_SEPARATORS:
515 mp_serializer_set_show_thousands_separators(self, g_value_get_boolean(value));
516 break;
517 case PROP_SHOW_TRAILING_ZEROES:
518 mp_serializer_set_show_trailing_zeroes(self, g_value_get_boolean(value));
519 break;
520 case PROP_BASE:
521 mp_serializer_set_base(self, g_value_get_int(value));
522 break;
523 default:
524 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
525 break;
530 static void
531 mp_serializer_get_property(GObject *object,
532 guint prop_id,
533 GValue *value,
534 GParamSpec *pspec)
536 MpSerializer *self = MP_SERIALIZER(object);
538 switch (prop_id) {
539 case PROP_SHOW_THOUSANDS_SEPARATORS:
540 g_value_set_boolean(value, mp_serializer_get_show_thousands_separators(self));
541 break;
542 case PROP_SHOW_TRAILING_ZEROES:
543 g_value_set_boolean(value, mp_serializer_get_show_trailing_zeroes(self));
544 break;
545 case PROP_BASE:
546 g_value_set_int(value, mp_serializer_get_base(self));
547 break;
548 default:
549 G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
550 break;
555 static void
556 mp_serializer_class_init(MpSerializerClass *klass)
558 GObjectClass *object_class = G_OBJECT_CLASS(klass);
559 object_class->get_property = mp_serializer_get_property;
560 object_class->set_property = mp_serializer_set_property;
562 g_type_class_add_private(klass, sizeof(MpSerializerPrivate));
564 g_object_class_install_property(object_class,
565 PROP_SHOW_THOUSANDS_SEPARATORS,
566 g_param_spec_boolean("show-thousands-separators",
567 "show-thousands-separators",
568 "Show thousands separators",
569 TRUE,
570 G_PARAM_READWRITE));
571 g_object_class_install_property(object_class,
572 PROP_SHOW_TRAILING_ZEROES,
573 g_param_spec_boolean("show-trailing-zeroes",
574 "show-trailing-zeroes",
575 "Show trailing zeroes",
576 FALSE,
577 G_PARAM_READWRITE));
578 g_object_class_install_property(object_class,
579 PROP_NUMBER_FORMAT,
580 g_param_spec_enum("number-format",
581 "number-format",
582 "Display format",
583 math_mp_display_format_get_type(),
584 MP_DISPLAY_FORMAT_AUTOMATIC,
585 G_PARAM_READWRITE));
586 g_object_class_install_property(object_class,
587 PROP_BASE,
588 g_param_spec_int("base",
589 "base",
590 "Default number base",
591 2, 16, 10,
592 G_PARAM_READWRITE));
596 static void
597 mp_serializer_init(MpSerializer *serializer)
599 gchar *radix, *tsep;
600 serializer->priv = G_TYPE_INSTANCE_GET_PRIVATE(serializer, mp_serializer_get_type(), MpSerializerPrivate);
602 radix = nl_langinfo(RADIXCHAR);
603 serializer->priv->radix = radix ? g_utf8_get_char(g_locale_to_utf8(radix, -1, NULL, NULL, NULL)) : '.';
604 tsep = nl_langinfo(THOUSEP);
605 if (tsep && tsep[0] != '\0')
606 serializer->priv->tsep = g_utf8_get_char(g_locale_to_utf8(tsep, -1, NULL, NULL, NULL));
607 else
608 serializer->priv->tsep = ' ';
609 serializer->priv->tsep_count = 3;
611 serializer->priv->base = 10;
612 serializer->priv->leading_digits = 12;
613 serializer->priv->trailing_digits = 9;
614 serializer->priv->show_zeroes = FALSE;
615 serializer->priv->show_tsep = FALSE;
616 serializer->priv->format = MP_DISPLAY_FORMAT_AUTOMATIC;