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)
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
21 #include <glib-object.h>
25 #include "mp-serializer.h"
27 #include "math-enums.h"
31 PROP_SHOW_THOUSANDS_SEPARATORS
,
32 PROP_SHOW_TRAILING_ZEROES
,
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
);
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
);
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
;
74 if (mp_is_negative(x
))
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 */
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
);
101 mp_divide_integer(&temp
, base
, &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
] : '?');
111 if (serializer
->priv
->show_tsep
&& i
== serializer
->priv
->tsep_count
) {
112 g_string_prepend_unichar(string
, serializer
->priv
->tsep
);
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
--) {
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
]);
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
, "−");
149 g_string_prepend(string
, "+");
152 /* Append base suffix if not in default base */
153 if (base
!= serializer
->priv
->base
) {
154 const gchar
*digits
[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"};
158 while (base
/ multiplier
!= 0)
160 while (multiplier
!= 1) {
164 g_string_append(string
, digits
[d
]);
172 mp_cast_to_string(MpSerializer
*serializer
, const MPNumber
*x
)
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
)) {
184 gboolean force_sign
= TRUE
;
187 mp_imaginary_component(x
, &x_im
);
189 if (strcmp(string
->str
, "0") == 0) {
190 g_string_assign(string
, "");
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) {
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");
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
);
227 mp_cast_to_exponential_string(MpSerializer
*serializer
, const MPNumber
*x
, gboolean eng_format
)
230 MPNumber t
, z
, base
, base3
, base10
, base10inv
, mantissa
;
234 const gchar
*super_digits
[] = {"⁰", "¹", "²", "³", "⁴", "⁵", "⁶", "⁷", "⁸", "⁹"};
236 string
= g_string_sized_new(1024);
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
)) {
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))) {
258 mp_divide(&mantissa
, &base
, &mantissa
);
261 while (!eng_format
&& mp_is_less_than(&mantissa
, &base10inv
)) {
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)) {
269 mp_multiply(&mantissa
, &base
, &mantissa
);
273 fixed
= mp_cast_to_string(serializer
, &mantissa
);
274 g_string_append(string
, fixed
);
279 g_string_append_printf(string
, "×10"); // FIXME: Use the current base
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
);
299 mp_serializer_to_string(MpSerializer
*serializer
, const MPNumber
*x
)
302 switch(serializer
->priv
->format
) {
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))
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
);
329 mp_serializer_from_string(MpSerializer
*serializer
, const gchar
*str
, MPNumber
*z
)
331 return mp_set_from_string(str
, serializer
->priv
->base
, z
);
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
;
350 mp_serializer_set_radix(MpSerializer
*serializer
, gunichar radix
)
352 serializer
->priv
->radix
= radix
;
357 mp_serializer_get_radix(MpSerializer
*serializer
)
359 return serializer
->priv
->radix
;
364 mp_serializer_set_thousands_separator(MpSerializer
*serializer
, gunichar separator
)
366 serializer
->priv
->tsep
= separator
;
371 mp_serializer_get_thousands_separator(MpSerializer
*serializer
)
373 return serializer
->priv
->tsep
;
378 mp_serializer_get_thousands_separator_count(MpSerializer
*serializer
)
380 return serializer
->priv
->tsep_count
;
385 mp_serializer_set_show_thousands_separators(MpSerializer
*serializer
, gboolean visible
)
387 serializer
->priv
->show_tsep
= visible
;
392 mp_serializer_get_show_thousands_separators(MpSerializer
*serializer
)
394 return serializer
->priv
->show_tsep
;
399 mp_serializer_set_show_trailing_zeroes(MpSerializer
*serializer
, gboolean visible
)
401 serializer
->priv
->show_zeroes
= visible
;
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
;
420 mp_serializer_set_accuracy(MpSerializer
*serializer
, int accuracy
)
422 serializer
->priv
->accuracy
= accuracy
;
427 mp_serializer_get_number_format(MpSerializer
*serializer
)
429 return serializer
->priv
->format
;
434 mp_serializer_set_number_format(MpSerializer
*serializer
, MpDisplayFormat format
)
436 serializer
->priv
->format
= format
;
441 mp_serializer_set_property(GObject
*object
,
446 MpSerializer
*self
= MP_SERIALIZER(object
);
449 case PROP_SHOW_THOUSANDS_SEPARATORS
:
450 mp_serializer_set_show_thousands_separators(self
, g_value_get_boolean(value
));
452 case PROP_SHOW_TRAILING_ZEROES
:
453 mp_serializer_set_show_trailing_zeroes(self
, g_value_get_boolean(value
));
456 mp_serializer_set_base(self
, g_value_get_int(value
));
459 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
466 mp_serializer_get_property(GObject
*object
,
471 MpSerializer
*self
= MP_SERIALIZER(object
);
474 case PROP_SHOW_THOUSANDS_SEPARATORS
:
475 g_value_set_boolean(value
, mp_serializer_get_show_thousands_separators(self
));
477 case PROP_SHOW_TRAILING_ZEROES
:
478 g_value_set_boolean(value
, mp_serializer_get_show_trailing_zeroes(self
));
481 g_value_set_int(value
, mp_serializer_get_base(self
));
484 G_OBJECT_WARN_INVALID_PROPERTY_ID(object
, prop_id
, pspec
);
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",
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",
515 g_object_class_install_property(object_class
,
517 g_param_spec_enum("number-format",
521 MP_DISPLAY_FORMAT_AUTOMATIC
,
523 g_object_class_install_property(object_class
,
525 g_param_spec_int("base",
527 "Default number base",
534 mp_serializer_init(MpSerializer
*serializer
)
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
));
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
;