2 * Copyright (C) 2010 Robin Sonefors
3 * Copyright (C) 2008-2012 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
12 public enum DisplayFormat
20 public class Serializer
: Object
22 private int leading_digits
; /* Number of digits to show before radix */
23 private int trailing_digits
; /* Number of digits to show after radix */
24 private DisplayFormat format
; /* Number display mode. */
25 private bool show_tsep
; /* Set if the thousands separator should be shown. */
26 private bool show_zeroes
; /* Set if trailing zeroes should be shown. */
28 private int number_base
; /* Numeric base */
30 private unichar radix
; /* Locale specific radix string. */
31 private unichar tsep
; /* Locale specific thousands separator. */
32 private int tsep_count
; /* Number of digits between separator. */
34 public Serializer (DisplayFormat format
, int number_base
, int trailing_digits
)
36 var radix_string
= nl_langinfo (NLItem
.RADIXCHAR
);
37 if (radix_string
!= null && radix_string
!= "")
38 radix
= radix_string
.locale_to_utf8 (-1, null, null).get_char (0);
41 var tsep_string
= nl_langinfo (NLItem
.THOUSEP
);
42 if (tsep_string
!= null && tsep_string
!= "")
43 tsep
= tsep_string
.locale_to_utf8 (-1, null, null).get_char (0);
48 this
.number_base
= number_base
;
50 this
.trailing_digits
= trailing_digits
;
56 public string to_string (Number x
)
61 case DisplayFormat
.AUTOMATIC
:
63 var s0
= cast_to_string (x
, ref n_digits
);
64 if (n_digits
<= leading_digits
)
67 return cast_to_exponential_string (x
, false, ref n_digits
);
68 case DisplayFormat
.FIXED
:
70 return cast_to_string (x
, ref n_digits
);
71 case DisplayFormat
.SCIENTIFIC
:
73 return cast_to_exponential_string (x
, false, ref n_digits
);
74 case DisplayFormat
.ENGINEERING
:
76 return cast_to_exponential_string (x
, true, ref n_digits
);
80 public Number?
from_string (string str
)
82 // FIXME: Move mp_set_from_string into here
83 return mp_set_from_string (str
, number_base
);
86 public void set_base (int number_base
)
88 this
.number_base
= number_base
;
91 public int get_base ()
96 public void set_radix (unichar radix
)
101 public unichar
get_radix ()
106 public void set_thousands_separator (unichar separator
)
111 public unichar
get_thousands_separator ()
116 public int get_thousands_separator_count ()
121 public void set_show_thousands_separators (bool visible
)
126 public bool get_show_thousands_separators ()
131 public void set_show_trailing_zeroes (bool visible
)
133 show_zeroes
= visible
;
136 public bool get_show_trailing_zeroes ()
141 public int get_leading_digits ()
143 return leading_digits
;
146 public void set_leading_digits (int leading_digits
)
148 this
.leading_digits
= leading_digits
;
151 public int get_trailing_digits ()
153 return trailing_digits
;
156 public void set_trailing_digits (int trailing_digits
)
158 this
.trailing_digits
= trailing_digits
;
161 public DisplayFormat
get_number_format ()
166 public void set_number_format (DisplayFormat format
)
168 this
.format
= format
;
171 private string cast_to_string (Number x
, ref int n_digits
)
173 var string = new StringBuilder
.sized (1024);
175 var x_real
= x
.real_component ();
176 cast_to_string_real (x_real
, number_base
, false, ref n_digits
, string);
179 var x_im
= x
.imaginary_component ();
181 var force_sign
= true;
182 if (string.str
== "0")
188 var s
= new StringBuilder
.sized (1024);
189 int n_complex_digits
= 0;
190 cast_to_string_real (x_im
, 10, force_sign
, ref n_complex_digits
, s
);
191 if (n_complex_digits
> n_digits
)
192 n_digits
= n_complex_digits
;
193 if (s
.str
== "0" || s
.str
== "+0" || s
.str
== "−0")
197 else if (s
.str
== "1")
201 else if (s
.str
== "+1")
203 string.append ("+i");
205 else if (s
.str
== "−1")
207 string.append ("−i");
213 else if (s
.str
!= "0")
214 string.append (s
.str
);
223 private void cast_to_string_real (Number x
, int number_base
, bool force_sign
, ref int n_digits
, StringBuilder
string)
225 const char digits
[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'};
228 if (number
.is_negative ())
229 number
= number
.abs ();
231 /* Add rounding factor */
232 var temp
= new Number
.integer (number_base
);
233 temp
= temp
.xpowy_integer (-(trailing_digits
+1));
234 temp
= temp
.multiply_integer (number_base
);
235 temp
= temp
.divide_integer (2);
236 var rounded_number
= number
.add (temp
);
238 /* Write out the integer component least significant digit to most */
239 temp
= rounded_number
.floor ();
243 if (this
.number_base
== 10 && show_tsep
&& i
== tsep_count
)
245 string.prepend_unichar (tsep
);
250 var t
= temp
.divide_integer (number_base
);
252 var t2
= t
.multiply_integer (number_base
);
254 var t3
= temp
.subtract (t2
);
256 var d
= t3
.to_integer ();
257 string.prepend_c (d
< 16 ? digits
[d
] : '?');
261 } while (!temp
.is_zero ());
263 var last_non_zero
= string.len
;
265 string.append_unichar (radix
);
267 /* Write out the fractional component */
268 temp
= rounded_number
.fractional_component ();
269 for (i
= 0; i
< trailing_digits
; i
++)
274 temp
= temp
.multiply_integer (number_base
);
275 var digit
= temp
.floor ();
276 var d
= digit
.to_integer ();
278 string.append_c (digits
[d
]);
281 last_non_zero
= string.len
;
282 temp
= temp
.subtract (digit
);
285 /* Strip trailing zeroes */
286 if (!show_zeroes
|| trailing_digits
== 0)
287 string.truncate (last_non_zero
);
289 /* Add sign on non-zero values */
290 if (string.str
!= "0" || force_sign
)
292 if (x
.is_negative ())
293 string.prepend ("−");
295 string.prepend ("+");
298 /* Append base suffix if not in default base */
299 if (number_base
!= this
.number_base
)
301 const string sub_digits
[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"};
305 while (number_base
/ multiplier
!= 0)
307 while (multiplier
!= 1)
312 string.append (sub_digits
[d
]);
318 private int cast_to_exponential_string_real (Number x
, StringBuilder
string, bool eng_format
, ref int n_digits
)
320 if (x
.is_negative ())
323 var mantissa
= x
.abs ();
325 var base_
= new Number
.integer (number_base
);
326 var base3
= base_
.xpowy_integer (3);
327 var base10
= base_
.xpowy_integer (10);
328 var t
= new Number
.integer (1);
329 var base10inv
= t
.divide (base10
);
332 if (!mantissa
.is_zero ())
334 while (!eng_format
&& mantissa
.compare (base10
) >= 0)
337 mantissa
= mantissa
.multiply (base10inv
);
340 while ((!eng_format
&& mantissa
.compare (base_
) >= 0) ||
341 (eng_format
&& (mantissa
.compare (base3
) >= 0 || exponent
% 3 != 0)))
344 mantissa
= mantissa
.divide (base_
);
347 while (!eng_format
&& mantissa
.compare (base10inv
) < 0)
350 mantissa
= mantissa
.multiply (base10
);
353 t
= new Number
.integer (1);
354 while (mantissa
.compare (t
) < 0 || (eng_format
&& exponent
% 3 != 0))
357 mantissa
= mantissa
.multiply (base_
);
361 string.append (cast_to_string (mantissa
, ref n_digits
));
366 private string cast_to_exponential_string (Number x
, bool eng_format
, ref int n_digits
)
368 var string = new StringBuilder
.sized (1024);
370 var x_real
= x
.real_component ();
371 var exponent
= cast_to_exponential_string_real (x_real
, string, eng_format
, ref n_digits
);
372 append_exponent (string, exponent
);
376 var x_im
= x
.imaginary_component ();
378 if (string.str
== "0")
381 var s
= new StringBuilder
.sized (1024);
382 int n_complex_digits
= 0;
383 exponent
= cast_to_exponential_string_real (x_im
, s
, eng_format
, ref n_complex_digits
);
384 if (n_complex_digits
> n_digits
)
385 n_digits
= n_complex_digits
;
386 if (s
.str
== "0" || s
.str
== "+0" || s
.str
== "−0")
390 else if (s
.str
== "1")
394 else if (s
.str
== "+1")
396 string.append ("+i");
398 else if (s
.str
== "−1")
400 string.append ("−i");
406 else if (s
.str
!= "0")
407 string.append (s
.str
);
411 append_exponent (string, exponent
);
417 private void append_exponent (StringBuilder
string, int exponent
)
419 const unichar super_digits
[] = {'⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'};
424 string.append ("×10"); // FIXME: Use the current base
427 exponent
= -exponent
;
428 string.append_unichar ('⁻');
431 var super_value
= "%d".printf (exponent
);
432 for (var i
= 0; i
< super_value
.length
; i
++)
433 string.append_unichar (super_digits
[super_value
[i
] - '0']);