00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032 #include <vlCore/Say.hpp>
00033 #include <cmath>
00034
00035 using namespace vl;
00036
00037 SayArg::SayArg()
00038 {
00039 init();
00040 }
00041
00042 SayArg::SayArg(void* d)
00043 {
00044 init();
00045 ulonglong = reinterpret_cast<unsigned long long>(d);
00046 type = ULONGLONG;
00047 }
00048
00049 SayArg::SayArg(const std::string& d)
00050 {
00051 init();
00052 str = d.c_str();
00053 type = STRING;
00054 }
00055
00056 SayArg::SayArg(const unsigned char* d)
00057 {
00058 init();
00059 if (d)
00060 str = (const char*)d;
00061 type = STRING;
00062 }
00063
00064 SayArg::SayArg(const char* d)
00065 {
00066 init();
00067 if (d)
00068 str = d;
00069 type = STRING;
00070 }
00071
00072 SayArg::SayArg(const String& d)
00073 {
00074 init();
00075 str = d;
00076 type = STRING;
00077 }
00078
00079 SayArg::SayArg(double d)
00080 {
00081 init();
00082 float64 = d;
00083 type = FLOAT64;
00084 }
00085
00086 SayArg::SayArg(float d)
00087 {
00088 init();
00089 float64 = d;
00090 type = FLOAT64;
00091 }
00092
00093 SayArg::SayArg(unsigned char d)
00094 {
00095 init();
00096 ulonglong = d;
00097 type = ULONGLONG;
00098 }
00099
00100 SayArg::SayArg(signed char d)
00101 {
00102 init();
00103 slonglong = d;
00104 type = SLONGLONG;
00105 }
00106
00107 SayArg::SayArg(unsigned short d)
00108 {
00109 init();
00110 ulonglong = d;
00111 type = ULONGLONG;
00112 }
00113
00114 SayArg::SayArg(signed short d)
00115 {
00116 init();
00117 slonglong = d;
00118 type = SLONGLONG;
00119 }
00120
00121 SayArg::SayArg(unsigned int d)
00122 {
00123 init();
00124 ulonglong = d;
00125 type = ULONGLONG;
00126 }
00127
00128 SayArg::SayArg(signed int d)
00129 {
00130 init();
00131 slonglong = d;
00132 type = SLONGLONG;
00133 }
00134
00135 SayArg::SayArg(unsigned long d)
00136 {
00137 init();
00138 ulonglong = d;
00139 type = ULONGLONG;
00140 }
00141
00142 SayArg::SayArg(signed long d)
00143 {
00144 init();
00145 slonglong = d;
00146 type = SLONGLONG;
00147 }
00148
00149 SayArg::SayArg(unsigned long long d)
00150 {
00151 init();
00152 ulonglong = d;
00153 type = ULONGLONG;
00154 }
00155
00156 SayArg::SayArg(signed long long d)
00157 {
00158 init();
00159 slonglong = d;
00160 type = SLONGLONG;
00161 }
00162
00163 void SayArg::init()
00164 {
00165 type = NO_TYPE;
00166 float64 = 0;
00167 ulonglong = 0;
00168 slonglong = 0;
00169 }
00170
00171 String Say::parse( const Say& pset ) const
00172 {
00173 String out;
00174 String fmt = pset.format_string;
00175
00176 int param_idx = 0;
00177
00178 int eur = -1;
00179 int base = -1;
00180 int field = -1;
00181 int decimals = -1;
00182 int align = -1;
00183 int fill = -1;
00184 int plus = -1;
00185
00186 int fmtstart = -1;
00187
00188
00189 bool fmtdata = false;
00190 for(int i=0; i<(int)fmt.length(); ++i)
00191 {
00192 int ch = (i<(int)fmt.length()) ? (int)fmt[i+0] : -1;
00193 int next_ch = (i<(int)fmt.length()-1) ? (int)fmt[i+1] : -1;
00194 int nnext_ch = (i<(int)fmt.length()-2) ? (int)fmt[i+2] : -1;
00195
00196 if (!fmtdata)
00197 {
00198 if (ch == '%' && next_ch == '%')
00199 {
00200 out += '%';
00201 ++i;
00202 }
00203 else
00204 if (ch == '%')
00205 {
00206 if (param_idx < (int)pset.size())
00207 {
00208 fmtdata = true;
00209 fmtstart = i;
00210 }
00211 else
00212 {
00213 out += " !!!too few parameters: %";
00214 }
00215 }
00216 else
00217 if (ch >= 0)
00218 {
00219 out += (unsigned short)ch;
00220 }
00221 }
00222 else
00223 {
00224
00225 if(eur == -1)
00226 {
00227 if (ch == '$')
00228 {
00229 eur = 1;
00230 continue;
00231 }
00232 }
00233
00234 if (base == -1)
00235 {
00236 switch(ch)
00237 {
00238 case 'b': base = 2; break;
00239 case 'o': base = 8; break;
00240 case 'd': base = 10; break;
00241 case 'h': base = 16; break;
00242 }
00243 if (base != -1)
00244 {
00245 if (eur == -1)
00246 eur = 0;
00247 continue;
00248 }
00249 }
00250
00251 if (plus == -1)
00252 {
00253 switch(ch)
00254 {
00255 case '+': plus = 1; break;
00256 }
00257 if (plus != -1)
00258 {
00259 if (base == -1)
00260 base = 10;
00261 if (eur == -1)
00262 eur = 0;
00263 continue;
00264 }
00265 }
00266
00267 if (fill == -1)
00268 {
00269 switch(ch)
00270 {
00271 case '0': fill = '0'; break;
00272 case ' ': fill = ' '; break;
00273 }
00274 if (fill != -1)
00275 {
00276 if (base == -1)
00277 base = 10;
00278 if (plus == -1)
00279 plus = 0;
00280 if (eur == -1)
00281 eur = 0;
00282 continue;
00283 }
00284 }
00285
00286 if (field == -1)
00287 {
00288 if (ch >= '0' && ch <= '9')
00289 {
00290 field = ch - '0';
00291 if (next_ch >= '0' && next_ch <= '9')
00292 {
00293 field = field*10 + next_ch - '0';
00294 ++i;
00295 }
00296 }
00297
00298 if (field != -1)
00299 {
00300 if (base == -1)
00301 base = 10;
00302 if (plus == -1)
00303 plus = 0;
00304 if (fill == -1)
00305 fill = ' ';
00306 if (eur == -1)
00307 eur = 0;
00308 continue;
00309 }
00310 }
00311
00312 if (decimals == -1)
00313 {
00314 if(ch == '.')
00315 {
00316 if (next_ch >= '0' && next_ch <= '9')
00317 {
00318 decimals = next_ch - '0';
00319 ++i;
00320 if (nnext_ch >= '0' && nnext_ch <= '9')
00321 {
00322 decimals = decimals*10 + nnext_ch - '0';
00323 ++i;
00324 }
00325 }
00326 }
00327
00328 if (decimals != -1)
00329 {
00330 if (base == -1)
00331 base = 10;
00332 if (plus == -1)
00333 plus = 0;
00334 if (fill == -1)
00335 fill = ' ';
00336 if (field == -1)
00337 field = 0;
00338 if (eur == -1)
00339 eur = 0;
00340 continue;
00341 }
00342 }
00343
00344 if (align == -1)
00345 {
00346 if(ch == '=')
00347 align = 0;
00348 if(ch == '<')
00349 align = 1;
00350 if(ch == '>')
00351 align = 2;
00352
00353 if (align != -1)
00354 {
00355 if (base == -1)
00356 base = 10;
00357 if (plus == -1)
00358 plus = 0;
00359 if (fill == -1)
00360 fill = ' ';
00361 if (field == -1)
00362 field = 0;
00363 if (eur == -1)
00364 eur = 0;
00365 if (decimals == -1)
00366 {
00367 switch(pset[param_idx].type)
00368 {
00369 case SayArg::FLOAT64: decimals = 6; break;
00370 default: decimals = 0; break;
00371 }
00372 }
00373 continue;
00374 }
00375 }
00376
00377
00378
00379
00380 const SayArg& p = pset[param_idx];
00381
00382 if (ch == 'c')
00383 {
00384 if (fmtstart != i-1)
00385 out += " !!! '%c' does not need arguments !!! ";
00386
00387 switch(p.type)
00388 {
00389 case SayArg::FLOAT64: out += (char)p.float64; break;
00390 case SayArg::SLONGLONG: out += (char)p.slonglong; break;
00391 case SayArg::ULONGLONG: out += (char)p.ulonglong; break;
00392 default:
00393 out += " !!! wrong argument type for '%c' !!! ";
00394 break;
00395 }
00396
00397 }
00398 else
00399 if (ch == 's')
00400 {
00401 if (fmtstart != i-1)
00402 out += " !!! '%s' does not need arguments !!! ";
00403
00404 switch(p.type)
00405 {
00406 case SayArg::STRING: out += p.str; break;
00407 default:
00408 out += " !!! wrong argument type for '%s' !!! ";
00409 break;
00410 }
00411
00412 }
00413 else
00414 if (ch == 'n' || ch == 'N' || ch == 'e' || ch == 'E')
00415 {
00416
00417 if (param_idx<(int)pset.size())
00418 {
00419 if (decimals == -1)
00420 {
00421 switch(p.type)
00422 {
00423 case SayArg::FLOAT64: decimals = 6; break;
00424 default: decimals = 0; break;
00425 }
00426 }
00427
00428 if (base == -1)
00429 base = 10;
00430 if (field == -1)
00431 field = 0;
00432 if (decimals == -1)
00433 decimals = 0;
00434 if (fill == -1)
00435 fill = ' ';
00436 if (plus == -1)
00437 plus = 0;
00438 if (align == -1)
00439 align = 2;
00440 if (eur == -1)
00441 eur = 0;
00442
00443 switch(p.type)
00444 {
00445 case SayArg::FLOAT64: out += format(p.float64, base, field, decimals, align, fill, plus, ch, eur); break;
00446 case SayArg::SLONGLONG: out += format(p.slonglong, base, field, decimals, align, fill, plus, ch, eur); break;
00447 case SayArg::ULONGLONG: out += format(p.ulonglong, base, field, decimals, align, fill, plus, ch, eur); break;
00448 default:
00449 out += " !!! wrong argument type for '%n' !!! ";
00450 break;
00451 }
00452 }
00453 else
00454 {
00455 out += " !!!missing parameter!!! ";
00456 if (ch != -1)
00457 i--;
00458 }
00459 }
00460 else
00461 {
00462 out += " !!!format error: unexpected '";
00463 out += (char)ch;
00464 out += "' !!! ";
00465 }
00466
00467 fmtdata = false;
00468 align = -1;
00469 base = -1;
00470 field = -1;
00471 decimals = -1;
00472 align = -1;
00473 fill = -1;
00474 plus = -1;
00475 eur = -1;
00476
00477 param_idx++;
00478 }
00479 }
00480
00481 if (fmtdata)
00482 {
00483 out += " !!!truncated format!!! ";
00484 param_idx++;
00485 }
00486
00487 if (param_idx < (int)pset.size())
00488 out += " !!!too many parameters!!! ";
00489
00490 return out;
00491
00492 }
00493
00494 String Say::euronotation(const String& str, int base) const
00495 {
00496 String tmp;
00497 int pos = (int)str.length();
00498 if ( str.contains('.') )
00499 {
00500 while(pos--)
00501 {
00502 if (str[pos] == '.')
00503 {
00504 tmp.insert(0, ',');
00505 break;
00506 }
00507 tmp.insert(0, str[pos]);
00508 }
00509 if (pos < 0)
00510 pos = (int)str.length();
00511 }
00512
00513 int count = 0;
00514 int wait = 3;
00515 if (base == 2)
00516 wait = 4;
00517 if (base == 16)
00518 wait = 2;
00519 while(pos--)
00520 {
00521 if (count && count % wait == 0)
00522 {
00523 tmp.insert(0, '.');
00524 }
00525 tmp.insert(0, str[pos]);
00526 count ++;
00527 }
00528
00529 return tmp;
00530 }
00531
00532 String Say::format(unsigned long long n, int base, int field, int decimals, int align, int fill, int plus, int finalizer, int eur) const
00533 {
00534 if (field < 0)
00535 field = -field;
00536
00537 if (field > 1024)
00538 field = 1024;
00539
00540 if (decimals < 0)
00541 decimals = -decimals;
00542 if (decimals > 20)
00543 decimals = 20;
00544
00545 if (align != 0 && align != 1 && align != 2)
00546 align = 0;
00547
00548 if (base > 16)
00549 base = 16;
00550
00551 if (base < 2)
00552 base = 2;
00553
00554 String str;
00555
00556 const char* hex = "0123456789abcdef";
00557
00558
00559
00560 int k = base;
00561 do
00562 {
00563 int x = (int)(n % base);
00564 int c = x/(k/base);
00565 str.insert(0, hex[c]);
00566 n = n / base;
00567 }
00568 while(n);
00569
00570 if (decimals)
00571 {
00572 str += '.';
00573 int i = decimals;
00574 while(i--)
00575 str += '0';
00576 }
00577
00578 bool negative = false;
00579
00580 return pipeline(str, base, field, decimals, finalizer, align, eur, fill, negative, plus);
00581 }
00582
00583 String Say::format(signed long long nn, int base, int field, int decimals, int align, int fill, int plus, int finalizer, int eur) const
00584 {
00585 if (field < 0)
00586 field = -field;
00587
00588 if (field > 1024)
00589 field = 1024;
00590
00591 if (decimals < 0)
00592 decimals = -decimals;
00593 if (decimals > 20)
00594 decimals = 20;
00595
00596 if (align != 0 && align != 1 && align != 2)
00597 align = 0;
00598
00599 if (base > 16)
00600 base = 16;
00601
00602 if (base < 2)
00603 base = 2;
00604
00605 String str;
00606
00607 const char* hex = "0123456789abcdef";
00608
00609
00610
00611 bool negative = nn < 0;
00612 unsigned long long n;
00613
00614 if (nn<0 && -nn<0)
00615 n = (unsigned long long)nn;
00616 else
00617 if (nn<0)
00618 n = - nn;
00619 else
00620 n = nn;
00621
00622
00623
00624
00625 int k = base;
00626 do
00627 {
00628 int x = (int)(n % base);
00629 int c = x/(k/base);
00630 str.insert(0, hex[c]);
00631 n = n / base;
00632 }
00633 while(n);
00634
00635 if (decimals)
00636 {
00637 str += '.';
00638 int i = decimals;
00639 while(i--)
00640 str += '0';
00641 }
00642
00643 return pipeline(str, base, field, decimals, finalizer, align, eur, fill, negative, plus);
00644 }
00645
00646 String Say::format(double num, int base, int field, int decimals, int align, int fill, int plus, int finalizer, int eur) const
00647 {
00648 if (field < 0)
00649 field = -field;
00650 if (field > 1024)
00651 field = 1024;
00652
00653 if (decimals < 0)
00654 decimals = -decimals;
00655 if (decimals > 20)
00656 decimals = 20;
00657
00658 if (align != 0 && align != 1 && align != 2)
00659 align = 0;
00660
00661 if (base > 16)
00662 base = 16;
00663
00664 if (base < 2)
00665 base = 2;
00666
00667 String str;
00668
00669 const char* hex = "0123456789abcdef";
00670
00671 double f = num;
00672
00673
00674
00675
00676 float tmp = (float)f;
00677 unsigned char *nan= (unsigned char*)&tmp;
00678 const char* sign = nan[3] >= 128 ? "-" : "+";
00679 unsigned char exp = (nan[3] << 1) + (nan[2] >> 7);
00680 nan[2] &= 127;
00681 unsigned int frac = nan[0] + (nan[1] << 8) + (nan[2] << 16);
00682
00683 bool negative = false;
00684 if (exp == 255 && frac == 0)
00685 {
00686 return String(sign) + "#INF";
00687 }
00688 else
00689 if (exp == 255 && frac != 0)
00690 {
00691 return "#NAN";
00692 }
00693 else
00694 {
00695
00696
00697 if (finalizer == 'n' || finalizer == 'N')
00698 {
00699 double fp = f - floor(f);
00700 double eps = base/2;
00701 int dec = decimals;
00702 do
00703 {
00704 if ( !(dec--) )
00705 break;
00706
00707 int c = (int)(fp * base);
00708 fp = fp * base - c;
00709
00710 eps /= base;
00711
00712 if (c<0 || c>15)
00713 {
00714 return "#ERR";
00715 }
00716
00717 if (dec == 0)
00718 {
00719
00720 f += eps/base;
00721 break;
00722 }
00723 }
00724 while(fp>0);
00725 }
00726
00727 if (f < 0)
00728 {
00729 f = -f;
00730 negative = true;
00731 }
00732 double n = floor(f);
00733
00734
00735
00736 int count = 0;
00737 unsigned int base2 = base*base;
00738 unsigned int base3 = base*base*base;
00739 unsigned int base4 = base*base*base*base;
00740 unsigned int base5 = base*base*base*base*base;
00741 unsigned int base6 = base*base*base*base*base*base;
00742 unsigned int base7 = base*base*base*base*base*base*base;
00743 while (floor(n))
00744 {
00745 if (n>=base7)
00746 {
00747 n /= base7;
00748 count+=7;
00749 }
00750 else
00751 if (n>=base6)
00752 {
00753 n /= base6;
00754 count+=6;
00755 }
00756 else
00757 if (n>=base5)
00758 {
00759 n /= base5;
00760 count+=5;
00761 }
00762 else
00763 if (n>=base4)
00764 {
00765 n /= base4;
00766 count+=4;
00767 }
00768 else
00769 if (n>=base3)
00770 {
00771 n /= base3;
00772 count+=3;
00773 }
00774 else
00775 if (n>=base2)
00776 {
00777 n /= base2;
00778 count+=2;
00779 }
00780 else
00781 {
00782 n = n / base;
00783 count++;
00784 }
00785 }
00786
00787
00788 double eps = (base / 2.0) / base;
00789 for(int i=0; i<count; ++i)
00790 {
00791 eps /= base;
00792 }
00793 n+=eps;
00794
00795 if (count)
00796 {
00797 do
00798 {
00799 int c = (int)(n * (double)base);
00800 n = n * (double)base - floor(n * (double)base);
00801 int next = (int)(n * base);
00802
00803 if (c<0 || c>15 || next<0 || next>15)
00804 {
00805 return "#ERR";
00806 }
00807
00808 str += hex[c];
00809 }
00810 while(--count);
00811 }
00812 else
00813 str += '0';
00814
00815 str += '.';
00816
00817
00818
00819 double fp = f - floor(f);
00820 do
00821 {
00822 int c = (int)(fp * base);
00823 fp = fp * base - c;
00824
00825 if (c<0 || c>15)
00826 {
00827 return "#ERR";
00828 }
00829
00830 str += hex[c];
00831 }
00832 while(fp>0);
00833
00834
00835
00836
00837
00838
00839
00840
00841
00842
00843 return pipeline(str, base, field, decimals, finalizer, align, eur, fill, negative, plus);
00844 }
00845 }
00846
00847 String Say::pipeline(const String& in_str, int base, int field, int decimals, int finalizer, int align, int eur, int fill, int negative, int plus) const
00848 {
00849 String str = in_str;
00850
00851
00852 int shift = 0;
00853
00854 if (finalizer == 'e' || finalizer == 'E')
00855 {
00856 int ptpos = (int)str.length();
00857 int nzpos = -1;
00858 for(int i=0; i<(int)str.length(); ++i)
00859 {
00860 if(str[i] != '0' && nzpos == -1 && str[i] != '.')
00861 nzpos = i;
00862 else
00863 if (str[i] == '.')
00864 ptpos = i;
00865 }
00866
00867 if (nzpos == -1)
00868 shift = 0;
00869 else
00870 shift = ptpos - nzpos - ( (ptpos > nzpos) ? 1 : 0 );
00871
00872
00873 str.remove( ptpos, 1 );
00874
00875
00876 while( str.length() && str[0] == '0' )
00877 str.remove(0);
00878
00879
00880
00881 if (str.length() == 1)
00882 str += '0';
00883 if (str.length() == 0)
00884 str = "00";
00885
00886 str.insert(1, '.');
00887 }
00888
00889
00890
00891
00892 if ( !str.contains('.') )
00893 str += ".0";
00894 int pos = str.find('.');
00895
00896 int decs = (int)str.length() - pos -1;
00897
00898 if (decs > decimals)
00899 {
00900
00901 int dot = decimals == 0 ? 1 : 0;
00902 str.resize(str.length() - (decs - decimals + dot));
00903 }
00904 else
00905 {
00906
00907 int i = decimals - decs;
00908 while(i--)
00909 str += '0';
00910 }
00911
00912
00913
00914 if (finalizer == 'e' || finalizer == 'E')
00915 {
00916 str += 'e';
00917 str += format((signed long long)shift, base, 0, 0, 2, 0, 1, 0,0);
00918 }
00919 else
00920
00921
00922 if (eur)
00923 str = euronotation(str, base);
00924
00925
00926
00927 int right = (field - (int)str.length()) / 2;
00928 right = right < 0 ? 0 : right;
00929
00930 int left = (field - (int)str.length()) - right;
00931 left = left < 0 ? 0 : left;
00932
00933 if (align == 1)
00934 {
00935 right += left;
00936 left = 0;
00937 }
00938 else
00939 if (align == 2)
00940 {
00941 left += right;
00942 right = 0;
00943 }
00944
00945
00946 str.insert(0, (wchar_t)fill, left);
00947
00948
00949 str.append(fill, right);
00950
00951 if (negative)
00952 {
00953 if (left)
00954 str.remove(0);
00955 else
00956 if (right)
00957 str.resize(str.length()-1);
00958
00959 str.insert(0, '-');
00960 }
00961 else
00962 if(plus)
00963 {
00964 if (left)
00965 str.remove(0);
00966 else
00967 if (right)
00968 str.resize(str.length()-1);
00969
00970 str.insert(0, '+');
00971 }
00972
00973
00974
00975 if (finalizer == 'N' || finalizer == 'E')
00976 {
00977 for(int i=0; i<(int)str.length(); ++i)
00978 if (str[i] >= 'a' && str[i] <= 'z')
00979 str[i] = str[i] - 'a' + 'A';
00980 }
00981
00982 return str;
00983 }
00984