2 * Copyright (C) 2010 Robin Sonefors
3 * Copyright (C) 2008-2011 Robert Ancell.
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
14 #include <glib-object.h>
18 #include "mp-serializer.h"
23 PROP_SHOW_THOUSANDS_SEPARATORS
,
24 PROP_SHOW_TRAILING_ZEROES
,
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
);
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
);
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
;
65 if (mp_is_negative(x
))
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 */
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
);
92 if (serializer
->priv
->base
== 10 && serializer
->priv
->show_tsep
&& i
== serializer
->priv
->tsep_count
) {
93 g_string_prepend_unichar(string
, serializer
->priv
->tsep
);
98 mp_divide_integer(&temp
, base
, &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
] : '?');
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
--) {
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
]);
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
, "−");
141 g_string_prepend(string
, "+");
144 /* Append base suffix if not in default base */
145 if (base
!= serializer
->priv
->base
) {
146 const gchar
*digits
[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"};
150 while (base
/ multiplier
!= 0)
152 while (multiplier
!= 1) {
156 g_string_append(string
, digits
[d
]);
164 mp_cast_to_string(MpSerializer
*serializer
, const MPNumber
*x
, int *n_digits
)
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
)) {
176 gboolean force_sign
= TRUE
;
178 int n_complex_digits
;
180 mp_imaginary_component(x
, &x_im
);
182 if (strcmp(string
->str
, "0") == 0) {
183 g_string_assign(string
, "");
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) {
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");
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
);
222 mp_cast_to_exponential_string_real(MpSerializer
*serializer
, const MPNumber
*x
, GString
*string
, gboolean eng_format
, int *n_digits
)
225 MPNumber t
, z
, base
, base3
, base10
, base10inv
, mantissa
;
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
)) {
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))) {
248 mp_divide(&mantissa
, &base
, &mantissa
);
251 while (!eng_format
&& mp_is_less_than(&mantissa
, &base10inv
)) {
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)) {
259 mp_multiply(&mantissa
, &base
, &mantissa
);
263 fixed
= mp_cast_to_string(serializer
, &mantissa
, n_digits
);
264 g_string_append(string
, fixed
);
272 append_exponent(GString
*string
, int exponent
)
274 const gchar
*super_digits
[] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"};
275 gchar
*super_value
, *c
;
280 g_string_append_printf(string
, "×10"); // FIXME: Use the current base
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
);
294 mp_cast_to_exponential_string(MpSerializer
*serializer
, const MPNumber
*x
, gboolean eng_format
, int *n_digits
)
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
)) {
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) {
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");
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
);
353 mp_serializer_to_string(MpSerializer
*serializer
, const MPNumber
*x
)
358 switch(serializer
->priv
->format
) {
360 case MP_DISPLAY_FORMAT_AUTOMATIC
:
361 s0
= mp_cast_to_string(serializer
, x
, &n_digits
);
362 if (n_digits
<= serializer
->priv
->leading_digits
)
366 return mp_cast_to_exponential_string(serializer
, x
, FALSE
, &n_digits
);
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
);
380 mp_serializer_from_string(MpSerializer
*serializer
, const gchar
*str
, MPNumber
*z
)
382 return mp_set_from_string(str
, serializer
->priv
->base
, z
);
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
;
401 mp_serializer_set_radix(MpSerializer
*serializer
, gunichar radix
)
403 serializer
->priv
->radix
= radix
;
408 mp_serializer_get_radix(MpSerializer
*serializer
)
410 return serializer
->priv
->radix
;
415 mp_serializer_set_thousands_separator(MpSerializer
*serializer
, gunichar separator
)
417 serializer
->priv
->tsep
= separator
;
422 mp_serializer_get_thousands_separator(MpSerializer
*serializer
)
424 return serializer
->priv
->tsep
;
429 mp_serializer_get_thousands_separator_count(MpSerializer
*serializer
)
431 return serializer
->priv
->tsep_count
;
436 mp_serializer_set_show_thousands_separators(MpSerializer
*serializer
, gboolean visible
)
438 serializer
->priv
->show_tsep
= visible
;
443 mp_serializer_get_show_thousands_separators(MpSerializer
*serializer
)
445 return serializer
->priv
->show_tsep
;
450 mp_serializer_set_show_trailing_zeroes(MpSerializer
*serializer
, gboolean visible
)
452 serializer
->priv
->show_zeroes
= visible
;
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
;
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
;
485 mp_serializer_set_trailing_digits(MpSerializer
*serializer
, int trailing_digits
)
487 serializer
->priv
->trailing_digits
= trailing_digits
;
492 mp_serializer_get_number_format(MpSerializer
*serializer
)
494 return serializer
->priv
->format
;
499 mp_serializer_set_number_format(MpSerializer
*serializer
, MpDisplayFormat format
)
501 serializer
->priv
->format
= format
;
506 mp_serializer_set_property(GObject
*object
,
511 MpSerializer
*self
= MP_SERIALIZER(object
);
514 case PROP_SHOW_THOUSANDS_SEPARATORS
:
515 mp_serializer_set_show_thousands_separators(self
, g_value_get_boolean(value
));
517 case PROP_SHOW_TRAILING_ZEROES
:
518 mp_serializer_set_show_trailing_zeroes(self
, g_value_get_boolean(value
));
521 mp_serializer_set_base(self
, g_value_get_int(value
));
524 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
531 mp_serializer_get_property(GObject
*object
,
536 MpSerializer
*self
= MP_SERIALIZER(object
);
539 case PROP_SHOW_THOUSANDS_SEPARATORS
:
540 g_value_set_boolean(value
, mp_serializer_get_show_thousands_separators(self
));
542 case PROP_SHOW_TRAILING_ZEROES
:
543 g_value_set_boolean(value
, mp_serializer_get_show_trailing_zeroes(self
));
546 g_value_set_int(value
, mp_serializer_get_base(self
));
549 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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",
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",
578 g_object_class_install_property(object_class
,
580 g_param_spec_enum("number-format",
583 math_mp_display_format_get_type(),
584 MP_DISPLAY_FORMAT_AUTOMATIC
,
586 g_object_class_install_property(object_class
,
588 g_param_spec_int("base",
590 "Default number base",
597 mp_serializer_init(MpSerializer
*serializer
)
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
));
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
;