1/* 2 * Copyright (C) 2010 Robin Sonefors 3 * Copyright (C) 2008-2012 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 3 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 12public enum DisplayFormat 13{ 14 AUTOMATIC, 15 FIXED, 16 SCIENTIFIC, 17 ENGINEERING 18} 19 20public class Serializer : Object 21{ 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. */ 27 28 private int number_base; /* Numeric base */ 29 private uint representation_base;/* Representation base. */ 30 31 private unichar radix; /* Locale specific radix string. */ 32 private unichar tsep; /* Locale specific thousands separator. */ 33 private int tsep_count; /* Number of digits between separator. */ 34 35 /* is set when an error (for example precision error while converting) occurs */ 36 public string? error { get; set; default = null; } 37 38 public Serializer (DisplayFormat format, int number_base, int trailing_digits) 39 { 40 var radix_string = Posix.nl_langinfo (Posix.NLItem.RADIXCHAR); 41 if (radix_string != null && radix_string != "") { 42 var radix_utf8 = radix_string.locale_to_utf8 (-1, null, null); 43 if (radix_utf8 != null) 44 radix = radix_utf8.get_char (0); 45 else 46 radix = '.'; 47 } 48 else 49 radix = '.'; 50 var tsep_string = Posix.nl_langinfo (Posix.NLItem.THOUSEP); 51 if (tsep_string != null && tsep_string != "") { 52 var tsep_utf8 = tsep_string.locale_to_utf8 (-1, null, null); 53 if (tsep_utf8 != null) 54 tsep = tsep_utf8.get_char (0); 55 else 56 tsep = ' '; 57 } 58 else 59 tsep = ' '; 60 tsep_count = 3; 61 62 this.number_base = number_base; 63 this.representation_base = number_base; 64 leading_digits = 12; 65 this.trailing_digits = trailing_digits; 66 show_zeroes = false; 67 show_tsep = false; 68 this.format = format; 69 } 70 71 public string to_string (Number x) 72 { 73 /* For base conversion equation, use FIXED format. */ 74 if (representation_base != number_base) 75 { 76 int n_digits = 0; 77 return cast_to_string (x, ref n_digits); 78 } 79 switch (format) 80 { 81 default: 82 case DisplayFormat.AUTOMATIC: 83 int n_digits = 0; 84 var s0 = cast_to_string (x, ref n_digits); 85 /* Decide leading digits based on number_base. Support 64 bits in programming mode. */ 86 switch (get_base ()) 87 { 88 /* 64 digits for binary mode. */ 89 case 2: 90 if (n_digits <= 64) 91 return s0; 92 else 93 return cast_to_exponential_string (x, false, ref n_digits); 94 /* 22 digis for octal mode. */ 95 case 8: 96 if (n_digits <= 22) 97 return s0; 98 else 99 return cast_to_exponential_string (x, false, ref n_digits); 100 /* 16 digits for hexadecimal mode. */ 101 case 16: 102 if(n_digits <= 16) 103 return s0; 104 else 105 return cast_to_exponential_string (x, false, ref n_digits); 106 /* Use default leading_digits for base 10 numbers. */ 107 case 10: 108 default: 109 if (n_digits <= leading_digits) 110 return s0; 111 else 112 return cast_to_exponential_string (x, false, ref n_digits); 113 } 114 case DisplayFormat.FIXED: 115 int n_digits = 0; 116 return cast_to_string (x, ref n_digits); 117 case DisplayFormat.SCIENTIFIC: 118 if (representation_base == 10) 119 { 120 int n_digits = 0; 121 return cast_to_exponential_string (x, false, ref n_digits); 122 } 123 else 124 { 125 int n_digits = 0; 126 return cast_to_string (x, ref n_digits); 127 } 128 case DisplayFormat.ENGINEERING: 129 if (representation_base == 10) 130 { 131 int n_digits = 0; 132 return cast_to_exponential_string (x, true, ref n_digits); 133 } 134 else 135 { 136 int n_digits = 0; 137 return cast_to_string (x, ref n_digits); 138 } 139 } 140 } 141 142 public Number? from_string (string str) 143 { 144 // FIXME: Move mp_set_from_string into here 145 return mp_set_from_string (str, number_base); 146 } 147 148 public void set_base (int number_base) 149 { 150 this.number_base = number_base; 151 } 152 153 public int get_base () 154 { 155 return number_base; 156 } 157 158 public void set_representation_base (uint representation_base) 159 { 160 this.representation_base = representation_base; 161 } 162 163 public uint get_representation_base () 164 { 165 return representation_base; 166 } 167 168 public void set_radix (unichar radix) 169 { 170 this.radix = radix; 171 } 172 173 public unichar get_radix () 174 { 175 return radix; 176 } 177 178 public void set_thousands_separator (unichar separator) 179 { 180 tsep = separator; 181 } 182 183 public unichar get_thousands_separator () 184 { 185 return tsep; 186 } 187 188 public int get_thousands_separator_count () 189 { 190 return tsep_count; 191 } 192 193 public void set_thousands_separator_count (int count) 194 { 195 tsep_count = count; 196 } 197 198 public void set_show_thousands_separators (bool visible) 199 { 200 show_tsep = visible; 201 } 202 203 public bool get_show_thousands_separators () 204 { 205 return show_tsep; 206 } 207 208 public void set_show_trailing_zeroes (bool visible) 209 { 210 show_zeroes = visible; 211 } 212 213 public bool get_show_trailing_zeroes () 214 { 215 return show_zeroes; 216 } 217 218 public int get_leading_digits () 219 { 220 return leading_digits; 221 } 222 223 public void set_leading_digits (int leading_digits) 224 { 225 this.leading_digits = leading_digits; 226 } 227 228 public int get_trailing_digits () 229 { 230 return trailing_digits; 231 } 232 233 public void set_trailing_digits (int trailing_digits) 234 { 235 this.trailing_digits = trailing_digits; 236 } 237 238 public DisplayFormat get_number_format () 239 { 240 return format; 241 } 242 243 public void set_number_format (DisplayFormat format) 244 { 245 this.format = format; 246 } 247 248 private string cast_to_string (Number x, ref int n_digits) 249 { 250 var string = new StringBuilder.sized (1024); 251 252 var x_real = x.real_component (); 253 cast_to_string_real (x_real, (int) representation_base, false, ref n_digits, string); 254 if (x.is_complex ()) 255 { 256 var x_im = x.imaginary_component (); 257 258 var force_sign = true; 259 if (string.str == "0") 260 { 261 string.assign (""); 262 force_sign = false; 263 } 264 265 var s = new StringBuilder.sized (1024); 266 int n_complex_digits = 0; 267 cast_to_string_real (x_im, (int) representation_base, force_sign, ref n_complex_digits, s); 268 if (n_complex_digits > n_digits) 269 n_digits = n_complex_digits; 270 if (s.str == "0" || s.str == "+0" || s.str == "−0") 271 { 272 if (string.str == "") 273 { 274 string.append ("0"); // real component is empty, the imaginary very small, we shouldn't return blank 275 } 276 } 277 else if (s.str == "1") 278 { 279 string.append ("i"); 280 } 281 else if (s.str == "+1") 282 { 283 string.append ("+i"); 284 } 285 else if (s.str == "−1") 286 { 287 string.append ("−i"); 288 } 289 else 290 { 291 if (s.str == "+0") 292 string.append ("+"); 293 else if (s.str != "0") 294 string.append (s.str); 295 296 string.append ("i"); 297 } 298 } 299 300 return string.str; 301 } 302 303 private void cast_to_string_real (Number x, int number_base, bool force_sign, ref int n_digits, StringBuilder string) 304 { 305 const char digits[] = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; 306 307 var number = x; 308 if (number.is_negative ()) 309 number = number.abs (); 310 311 /* Add rounding factor */ 312 var temp = new Number.integer (number_base); 313 temp = temp.xpowy_integer (-(trailing_digits+1)); 314 temp = temp.multiply_integer (number_base); 315 temp = temp.divide_integer (2); 316 var rounded_number = number.add (temp); 317 318 /* Write out the integer component least significant digit to most */ 319 temp = rounded_number.floor (); 320 var i = 0; 321 do 322 { 323 if (number_base == 10 && show_tsep && i == tsep_count) 324 { 325 string.prepend_unichar (tsep); 326 i = 0; 327 } 328 i++; 329 330 var t = temp.divide_integer (number_base); 331 t = t.floor (); 332 var t2 = t.multiply_integer (number_base); 333 334 var t3 = temp.subtract (t2); 335 336 var d = t3.to_integer (); 337 338 if (d < 16 && d >= 0) 339 { 340 string.prepend_c (digits[d]); 341 } 342 else 343 { 344 string.prepend_c ('?'); 345 error = _("Overflow: the result couldn’t be calculated"); 346 string.assign ("0"); 347 break; 348 } 349 n_digits++; 350 351 temp = t; 352 } while (!temp.is_zero ()); 353 354 var last_non_zero = string.len; 355 356 string.append_unichar (radix); 357 358 /* Write out the fractional component */ 359 temp = rounded_number.fractional_component (); 360 for (i = 0; i < trailing_digits; i++) 361 { 362 if (temp.is_zero ()) 363 break; 364 365 temp = temp.multiply_integer (number_base); 366 var digit = temp.floor (); 367 var d = digit.to_integer (); 368 369 string.append_c (digits[d]); 370 371 if (d != 0) 372 last_non_zero = string.len; 373 temp = temp.subtract (digit); 374 } 375 376 /* Strip trailing zeroes */ 377 if (!show_zeroes || trailing_digits == 0) 378 string.truncate (last_non_zero); 379 380 /* Add sign on non-zero values */ 381 if (string.str != "0" || force_sign) 382 { 383 if (x.is_negative ()) 384 string.prepend ("−"); 385 else if (force_sign) 386 string.prepend ("+"); 387 } 388 389 /* Append base suffix if not in default base */ 390 if (number_base != this.number_base) 391 { 392 const string sub_digits[] = {"₀", "₁", "₂", "₃", "₄", "₅", "₆", "₇", "₈", "₉"}; 393 int multiplier = 1; 394 int b = number_base; 395 396 while (number_base / multiplier != 0) 397 multiplier *= 10; 398 while (multiplier != 1) 399 { 400 int d; 401 multiplier /= 10; 402 d = b / multiplier; 403 string.append (sub_digits[d]); 404 b -= d * multiplier; 405 } 406 } 407 } 408 409 private int cast_to_exponential_string_real (Number x, StringBuilder string, bool eng_format, ref int n_digits) 410 { 411 if (x.is_negative ()) 412 string.append ("−"); 413 414 var mantissa = x.abs (); 415 416 var base_ = new Number.integer (number_base); 417 var base3 = base_.xpowy_integer (3); 418 var base10 = base_.xpowy_integer (10); 419 var t = new Number.integer (1); 420 var base10inv = t.divide (base10); 421 422 var exponent = 0; 423 if (!mantissa.is_zero ()) 424 { 425 while (!eng_format && mantissa.compare (base10) >= 0) 426 { 427 exponent += 10; 428 mantissa = mantissa.multiply (base10inv); 429 } 430 431 while ((!eng_format && mantissa.compare (base_) >= 0) || 432 (eng_format && (mantissa.compare (base3) >= 0 || exponent % 3 != 0))) 433 { 434 exponent += 1; 435 mantissa = mantissa.divide (base_); 436 } 437 438 while (!eng_format && mantissa.compare (base10inv) < 0) 439 { 440 exponent -= 10; 441 mantissa = mantissa.multiply (base10); 442 } 443 444 t = new Number.integer (1); 445 while (mantissa.compare (t) < 0 || (eng_format && exponent % 3 != 0)) 446 { 447 exponent -= 1; 448 mantissa = mantissa.multiply (base_); 449 } 450 } 451 452 string.append (cast_to_string (mantissa, ref n_digits)); 453 454 return exponent; 455 } 456 457 private string cast_to_exponential_string (Number x, bool eng_format, ref int n_digits) 458 { 459 var string = new StringBuilder.sized (1024); 460 461 var x_real = x.real_component (); 462 var exponent = cast_to_exponential_string_real (x_real, string, eng_format, ref n_digits); 463 append_exponent (string, exponent); 464 465 if (x.is_complex ()) 466 { 467 var x_im = x.imaginary_component (); 468 469 if (string.str == "0") 470 string.assign (""); 471 472 var s = new StringBuilder.sized (1024); 473 int n_complex_digits = 0; 474 exponent = cast_to_exponential_string_real (x_im, s, eng_format, ref n_complex_digits); 475 if (n_complex_digits > n_digits) 476 n_digits = n_complex_digits; 477 if (s.str == "0" || s.str == "+0" || s.str == "−0") 478 { 479 /* Ignore */ 480 } 481 else if (s.str == "1") 482 { 483 string.append ("i"); 484 } 485 else if (s.str == "+1") 486 { 487 string.append ("+i"); 488 } 489 else if (s.str == "−1") 490 { 491 string.append ("−i"); 492 } 493 else 494 { 495 if (s.str == "+0") 496 string.append ("+"); 497 else if (s.str != "0") 498 string.append (s.str); 499 500 string.append ("i"); 501 } 502 append_exponent (string, exponent); 503 } 504 505 return string.str; 506 } 507 508 private void append_exponent (StringBuilder string, int exponent) 509 { 510 const unichar super_digits[] = {'⁰', '¹', '²', '³', '⁴', '⁵', '⁶', '⁷', '⁸', '⁹'}; 511 512 if (exponent == 0) 513 return; 514 515 string.append ("×10"); // FIXME: Use the current base 516 if (exponent < 0) 517 { 518 exponent = -exponent; 519 string.append_unichar ('⁻'); 520 } 521 522 var super_value = "%d".printf (exponent); 523 for (var i = 0; i < super_value.length; i++) 524 string.append_unichar (super_digits[super_value[i] - '0']); 525 } 526} 527