1 #include "main.h"
2 #include <cfenv>
3 #include <iostream>
4 
5 // run ncmetric, and then change any localized decimal separator into our
6 // proud imperial yankee capitalist democratic one dot under god period.
7 // manifest destiny, bitchhhhhhhezzzz!
impericize_ncmetric(uintmax_t val,uintmax_t decimal,char * buf,int omitdec,unsigned mult,int uprefix)8 char* impericize_ncmetric(uintmax_t val, uintmax_t decimal, char* buf,
9                           int omitdec, unsigned mult, int uprefix) {
10   const char* decisep = localeconv()->decimal_point;
11   REQUIRE(decisep);
12   REQUIRE(1 ==  strlen(decisep));
13   REQUIRE(ncnmetric(val, INT_MAX, decimal, buf, omitdec, mult, uprefix));
14   char* commie = buf;
15   while( (commie = strstr(commie, decisep)) ){
16     *commie = '.'; // https://dank.qemfd.net/images/16whcc.jpg
17     ++commie;
18   }
19   return buf;
20 }
21 
22 TEST_CASE("Metric") {
23   const char* decisep = localeconv()->decimal_point;
24   REQUIRE(decisep);
25   REQUIRE(1 ==  strlen(decisep));
26   REQUIRE(0 ==  fesetround(FE_TONEAREST));
27 
28   SUBCASE("CornerInts") {
29     char buf[NCPREFIXSTRLEN + 1];
30     impericize_ncmetric(0, 1, buf, 0, 1000, '\0');
31     CHECK(!strcmp("0.00", buf));
32     impericize_ncmetric(0, 1, buf, 0, 1024, 'i');
33     CHECK(!strcmp("0.00", buf)); // no suffix on < mult
34     impericize_ncmetric(1, 1, buf, 0, 1000, '\0');
35     CHECK(!strcmp("1.00", buf));
36     impericize_ncmetric(1, 1, buf, 0, 1024, 'i');
37     CHECK(!strcmp("1.00", buf));
38     impericize_ncmetric(0, 1, buf, 1, 1000, '\0');
39     CHECK(!strcmp("0", buf));
40     impericize_ncmetric(0, 1, buf, 1, 1024, 'i');
41     CHECK(!strcmp("0", buf)); // no suffix on < mult
42     impericize_ncmetric(1, 1, buf, 1, 1000, '\0');
43     CHECK(!strcmp("1", buf));
44     impericize_ncmetric(1, 1, buf, 1, 1024, 'i');
45     CHECK(!strcmp("1", buf));
46     impericize_ncmetric(999, 1, buf, 1, 1000, '\0');
47     CHECK(!strcmp("999", buf));
48     impericize_ncmetric(1000, 1, buf, 1, 1000, '\0');
49     CHECK(!strcmp("1K", buf));
50     impericize_ncmetric(1000, 1, buf, 1, 1000, 'i');
51     CHECK(!strcmp("1Ki", buf));
52     impericize_ncmetric(1000, 1, buf, 1, 1024, 'i');
53     CHECK(!strcmp("1000", buf)); // FIXME should be 0.977Ki
54     impericize_ncmetric(1023, 1, buf, 1, 1000, '\0');
55     CHECK(!strcmp("1.02K", buf));
56     impericize_ncmetric(1023, 1, buf, 1, 1024, 'i');
57     CHECK(!strcmp("1023", buf));
58     impericize_ncmetric(1024, 1, buf, 1, 1000, '\0');
59     CHECK(!strcmp("1.02K", buf));
60     impericize_ncmetric(1024, 1, buf, 1, 1024, 'i');
61     CHECK(!strcmp("1Ki", buf));
62     impericize_ncmetric(1025, 1, buf, 1, 1000, '\0');
63     CHECK(!strcmp("1.02K", buf));
64     impericize_ncmetric(1025, 1, buf, 0, 1024, 'i');
65     CHECK(!strcmp("1.00Ki", buf));
66     impericize_ncmetric(1025, 1, buf, 1, 1024, 'i');
67     CHECK(!strcmp("1.00Ki", buf));
68     impericize_ncmetric(4096, 1, buf, 1, 1000, '\0');
69     CHECK(!strcmp("4.10K", buf));
70     impericize_ncmetric(4096, 1, buf, 0, 1024, 'i');
71     CHECK(!strcmp("4.00Ki", buf));
72     impericize_ncmetric(4096, 1, buf, 1, 1024, 'i');
73     CHECK(!strcmp("4Ki", buf));
74   }
75 
76   SUBCASE("Maxints") {
77     char buf[NCPREFIXSTRLEN + 1];
78     // FIXME these will change based on the size of intmax_t and uintmax_t
79     impericize_ncmetric(INTMAX_MAX - 1, 1, buf, 0, 1000, '\0');
80     CHECK(!strcmp("9.22E", buf));
81     impericize_ncmetric(INTMAX_MAX, 1, buf, 0, 1000, '\0');
82     CHECK(!strcmp("9.22E", buf));
83     impericize_ncmetric(UINTMAX_MAX - 1, 1, buf, 0, 1000, '\0');
84     CHECK(!strcmp("18.45E", buf));
85     impericize_ncmetric(UINTMAX_MAX, 1, buf, 0, 1000, '\0');
86     CHECK(!strcmp("18.45E", buf));
87   }
88 
89   SUBCASE("Maxints1024") {
90     char buf[NCPREFIXSTRLEN + 1], gold[NCPREFIXSTRLEN + 1];
91     // FIXME these will change based on the size of intmax_t and uintmax_t
92     REQUIRE(ncbprefix(((double)(INTMAX_MAX - 1ull)), 1, buf, 0));
93     sprintf(gold, "%.2fEi", ((double)(INTMAX_MAX - 1ull)) / (1ull << 60));
94     CHECK(!strcmp(gold, buf));
95     REQUIRE(ncbprefix(((double)(INTMAX_MAX - (1ull << 53))), 1, buf, 0));
96     sprintf(gold, "%.2fEi", ((double)(INTMAX_MAX - (1ull << 53))) / (1ull << 60));
97     CHECK(!strcmp(gold, buf));
98     REQUIRE(ncbprefix(INTMAX_MAX + 1ull, 1, buf, 0));
99     sprintf(gold, "%.2fEi", ((double)(INTMAX_MAX + 1ull)) / (1ull << 60));
100     CHECK(!strcmp(gold, buf));
101     impericize_ncmetric(UINTMAX_MAX - 1, 1, buf, 0, 1024, 'i');
102     CHECK(!strcmp("16.00Ei", buf));
103     impericize_ncmetric(UINTMAX_MAX, 1, buf, 0, 1024, 'i');
104     CHECK(!strcmp("16.00Ei", buf));
105     ncbprefix(UINTMAX_MAX - (1ull << 53), 1, buf, 0);
106     sprintf(gold, "%.2fEi", ((double)UINTMAX_MAX - (1ull << 53)) / (1ull << 60));
107     CHECK(!strcmp(gold, buf));
108   }
109 
110   const char suffixes[] = "\0KMGTPE";
111 
112   SUBCASE("PowersOfTen") {
113     char gold[NCPREFIXSTRLEN + 1];
114     char buf[NCPREFIXSTRLEN + 1];
115     uintmax_t goldval = 1;
116     uintmax_t val = 1;
117     size_t i = 0;
118     do{
119       ncqprefix(val, 1, buf, 0);
120       const int sidx = i / 3;
121       snprintf(gold, sizeof(gold), "%ju%s00%c", goldval, decisep, suffixes[sidx]);
122       CHECK(!strcmp(gold, buf));
123       if(UINTMAX_MAX / val < 10){
124         break;
125       }
126       val *= 10;
127       if((goldval *= 10) == 1000){
128         goldval = 1;
129       }
130     }while(++i < sizeof(suffixes) * 3);
131     // If we ran through all our suffixes, that's a problem
132     CHECK(sizeof(suffixes) * 3 >  i);
133   }
134 
135   SUBCASE("PowersOfTenNoDec") {
136     char gold[NCPREFIXSTRLEN + 1];
137     char buf[NCPREFIXSTRLEN + 1];
138     uintmax_t goldval = 1;
139     uintmax_t val = 1;
140     size_t i = 0;
141     do{
142       ncqprefix(val, 1, buf, 1);
143       const int sidx = i / 3;
144       snprintf(gold, sizeof(gold), "%ju%c", goldval, suffixes[sidx]);
145       CHECK(!strcmp(gold, buf));
146       if(UINTMAX_MAX / val < 10){
147         break;
148       }
149       val *= 10;
150       if((goldval *= 10) == 1000){
151         goldval = 1;
152       }
153     }while(++i < sizeof(suffixes) * 3);
154     // If we ran through all our suffixes, that's a problem
155     CHECK(sizeof(suffixes) * 3 >  i);
156   }
157 
158   SUBCASE("PowersOfTwo") {
159     char gold[NCBPREFIXSTRLEN + 1];
160     char buf[NCBPREFIXSTRLEN + 1];
161     uintmax_t goldval = 1;
162     uintmax_t val = 1;
163     size_t i = 0;
164     do{
165       ncbprefix(val, 1, buf, 0);
166       const int sidx = i / 10;
167       snprintf(gold, sizeof(gold), "%ju%s00%ci", goldval, decisep, suffixes[sidx]);
168       CHECK(!strcmp(gold, buf));
169       if(UINTMAX_MAX / val < 10){
170         break;
171       }
172       val *= 2;
173       if((goldval *= 2) == 1024){
174         goldval = 1;
175       }
176     }while(++i < sizeof(suffixes) * 10);
177     // If we ran through all our suffixes, that's a problem
178     CHECK(sizeof(suffixes) * 10 >  i);
179   }
180 
181   SUBCASE("PowersOfTwoNoDec") {
182     char gold[NCBPREFIXSTRLEN + 1];
183     char buf[NCBPREFIXSTRLEN + 1];
184     uintmax_t goldval = 1;
185     uintmax_t val = 1;
186     size_t i = 0;
187     do{
188       ncbprefix(val, 1, buf, 1);
189       const int sidx = i / 10;
190       snprintf(gold, sizeof(gold), "%ju%ci", goldval, suffixes[sidx]);
191       CHECK(!strcmp(gold, buf));
192       if(UINTMAX_MAX / val < 10){
193         break;
194       }
195       val *= 2;
196       if((goldval *= 2) == 1024){
197         goldval = 1;
198       }
199     }while(++i < sizeof(suffixes) * 10);
200     // If we ran through all our suffixes, that's a problem
201     CHECK(sizeof(suffixes) * 10 >  i);
202   }
203 
204   SUBCASE("PowersOfTwoAsTens") {
205     char gold[NCPREFIXSTRLEN + 1];
206     char buf[NCPREFIXSTRLEN + 1];
207     uintmax_t vfloor = 1;
208     uintmax_t val = 1;
209     size_t i = 0;
210     do{
211       ncqprefix(val, 1, buf, 0);
212       const int sidx = i / 10;
213       snprintf(gold, sizeof(gold), "%.2f%c", ((double)val) / vfloor, suffixes[sidx]);
214       CHECK(!strcmp(gold, buf));
215       if(UINTMAX_MAX / val < 10){
216         break;
217       }
218       val *= 2;
219       if(i % 10 == 9){
220         vfloor *= 1000;
221       }
222     }while(++i < sizeof(suffixes) * 10);
223     // If we ran through all our suffixes, that's a problem
224     CHECK(sizeof(suffixes) * 10 >  i);
225   }
226 
227   SUBCASE("PowersOfTenAsTwos") {
228     char gold[NCBPREFIXSTRLEN + 1];
229     char buf[NCBPREFIXSTRLEN + 1];
230     uintmax_t vfloor = 1;
231     uintmax_t val = 1;
232     size_t i = 0;
233     do{
234       ncbprefix(val, 1, buf, 0);
235       const int sidx = i ? (i - 1) / 3 : 0;
236       snprintf(gold, sizeof(gold), "%.2f%ci", ((double)val) / vfloor, suffixes[sidx]);
237       CHECK(!strcmp(gold, buf));
238       if(UINTMAX_MAX / val < 10){
239         break;
240       }
241       val *= 10;
242       if(i && i % 3 == 0){
243         vfloor *= 1024;
244       }
245     }while(++i < sizeof(suffixes) * 10);
246     // If we ran through all our suffixes, that's a problem
247     CHECK(sizeof(suffixes) * 10 >  i);
248   }
249 
250   SUBCASE("PowersOfTenMinusOne") {
251     char gold[NCPREFIXSTRLEN + 1];
252     char buf[NCPREFIXSTRLEN + 1];
253     uintmax_t vfloor = 1;
254     uintmax_t val = 1;
255     size_t i = 0;
256     do{
257       ncqprefix(val - 1, 1, buf, 0);
258       const int sidx = i ? (i - 1) / 3 : 0;
259       snprintf(gold, sizeof(gold), "%.2f%c", ((double)(val - 1)) / vfloor, suffixes[sidx]);
260       CHECK(!strcmp(gold, buf));
261       if(UINTMAX_MAX / val < 10){
262         break;
263       }
264       val *= 10;
265       if(i && i % 3 == 0){
266         vfloor *= 1000;
267       }
268     }while(++i < sizeof(suffixes) * 3);
269     // If we ran through all our suffixes, that's a problem
270     CHECK(sizeof(suffixes) * 3 >  i);
271   }
272 
273   SUBCASE("PowersOfTenPlusOne") {
274     char gold[NCPREFIXSTRLEN + 1];
275     char buf[NCPREFIXSTRLEN + 1];
276     uintmax_t vfloor = 1;
277     uintmax_t val = 1;
278     size_t i = 0;
279     do{
280       ncqprefix(val + 1, 1, buf, 0);
281       const int sidx = i / 3;
282       snprintf(gold, sizeof(gold), "%.2f%c", ((double)(val + 1)) / vfloor, suffixes[sidx]);
283       CHECK(!strcmp(gold, buf));
284       if(UINTMAX_MAX / val < 10){
285         break;
286       }
287       val *= 10;
288       if(i % 3 == 2){
289         vfloor *= 1000;
290       }
291     }while(++i < sizeof(suffixes) * 3);
292     // If we ran through all our suffixes, that's a problem
293     CHECK(sizeof(suffixes) * 3 > i);
294   }
295 
296   SUBCASE("PowersOfTenMinusOneAsTwos") {
297     char gold[NCBPREFIXSTRLEN + 1];
298     char buf[NCBPREFIXSTRLEN + 1];
299     uintmax_t vfloor = 1;
300     uintmax_t val = 1;
301     size_t i = 0;
302     do{
303       ncbprefix(val - 1, 1, buf, 0);
304       const int sidx = i ? (i - 1) / 3 : 0;
305       snprintf(gold, sizeof(gold), "%.2f%ci", ((double)(val - 1)) / vfloor, suffixes[sidx]);
306       CHECK(!strcmp(gold, buf));
307       if(UINTMAX_MAX / val < 10){
308         break;
309       }
310       val *= 10;
311       if(i && i % 3 == 0){
312         vfloor *= 1024;
313       }
314     }while(++i < sizeof(suffixes) * 10);
315     // If we ran through all our suffixes, that's a problem
316     CHECK(sizeof(suffixes) * 10 > i);
317   }
318 
319   constexpr auto GIG = 1000000000ull;
320 
321   // Output ought be scaled down for output while maintaining precision during
322   // computation of that output. For instance, we might feed a number of
323   // nanoseconds, but want output in seconds.
324   // This requires 'decimal' = GIG.
325   SUBCASE("ScaledGigSupra") {
326     char gold[NCPREFIXSTRLEN + 1];
327     snprintf(gold, sizeof(gold), "%.2f", 9.029); // 9.02
328     char buf[NCPREFIXSTRLEN + 1];
329     uintmax_t val = 9027854993;
330     uintmax_t decimal = GIG;
331     REQUIRE(ncqprefix(val, decimal, buf, 0));
332     CHECK(!strcmp(buf, gold));
333   }
334 
335   SUBCASE("ScaledGigUnity") {
336     char gold[NCPREFIXSTRLEN + 1];
337     snprintf(gold, sizeof(gold), "%.2f", 1.0); // 1.00
338     char buf[NCPREFIXSTRLEN + 1];
339     uintmax_t decimal = GIG;
340     uintmax_t val = decimal;
341     REQUIRE(ncqprefix(val, decimal, buf, 0));
342     CHECK(!strcmp(buf, gold));
343   }
344 
345   SUBCASE("ScaledGigJustAbove") {
346     char gold[NCPREFIXSTRLEN + 1];
347     snprintf(gold, sizeof(gold), "%.2f", 1.0); // 1.00
348     char buf[NCPREFIXSTRLEN + 1];
349     uintmax_t val = 1000000001;
350     uintmax_t decimal = GIG;
351     REQUIRE(ncqprefix(val, decimal, buf, 0));
352     CHECK(!strcmp(buf, gold));
353   }
354 
355   SUBCASE("ScaledGigJustBelow") {
356     char gold[NCPREFIXSTRLEN + 1];
357     snprintf(gold, sizeof(gold), "%.2fm", 999.999); //999.99
358     char buf[NCPREFIXSTRLEN + 1];
359     uintmax_t val = 999999999;
360     uintmax_t decimal = GIG;
361     REQUIRE(ncqprefix(val, decimal, buf, 0));
362     CHECK(!strcmp(buf, gold));
363   }
364 
365   SUBCASE("ScaledGigSub") {
366     char gold[NCPREFIXSTRLEN + 1];
367     snprintf(gold, sizeof(gold), "%.2fm", 27.85); // 27.85
368     char buf[NCPREFIXSTRLEN + 1];
369     uintmax_t val = 27854993;
370     uintmax_t decimal = GIG;
371     REQUIRE(ncqprefix(val, decimal, buf, 0));
372     CHECK(!strcmp(buf, gold));
373   }
374 
375   SUBCASE("ScaledGigSubSub") {
376     char gold[NCPREFIXSTRLEN + 1];
377     snprintf(gold, sizeof(gold), "%.2fm", 7.85); // 7.85
378     char buf[NCPREFIXSTRLEN + 1];
379     uintmax_t val = 7854993;
380     uintmax_t decimal = GIG;
381     REQUIRE(ncqprefix(val, decimal, buf, 0));
382     CHECK(!strcmp(buf, gold));
383   }
384 
385   SUBCASE("SmallCorners") {
386     char buf[NCPREFIXSTRLEN + 1];
387     impericize_ncmetric(1, 1000, buf, 0, 1000, '\0');
388     CHECK(!strcmp("1.00m", buf));
389     impericize_ncmetric(1, 1024, buf, 0, 1024, 'i');
390     CHECK(!strcmp("1.00mi", buf));
391 
392     impericize_ncmetric(1, 1000000000000000000ull, buf, 0, 1000, '\0');
393     CHECK(!strcmp("1.00a", buf));
394 
395     impericize_ncmetric(19, 10000000000000000ull, buf, 0, 1000, '\0');
396     CHECK(!strcmp("1.90f", buf));
397     impericize_ncmetric(99, 10000000000000000ull, buf, 0, 1000, '\0');
398     CHECK(!strcmp("9.90f", buf));
399 
400     impericize_ncmetric(100, 10000000000000000ull, buf, 0, 1000, '\0');
401     CHECK(!strcmp("10.00f", buf));
402 
403     impericize_ncmetric(1, 10, buf, 0, 1000, '\0');
404     CHECK(!strcmp("100.00m", buf));
405     impericize_ncmetric(1, 100, buf, 0, 1000, '\0');
406     CHECK(!strcmp("10.00m", buf));
407     impericize_ncmetric(10, 100, buf, 0, 1000, '\0');
408     CHECK(!strcmp("100.00m", buf));
409     impericize_ncmetric(10, 1000, buf, 0, 1000, '\0');
410     CHECK(!strcmp("10.00m", buf));
411     impericize_ncmetric(100, 1000, buf, 0, 1000, '\0');
412     CHECK(!strcmp("100.00m", buf));
413     impericize_ncmetric(1000, 1000, buf, 0, 1000, '\0');
414     CHECK(!strcmp("1.00", buf));
415 
416     impericize_ncmetric(100, 1000000000000000ull, buf, 0, 1000, '\0');
417     CHECK(!strcmp("100.00f", buf));
418 
419     impericize_ncmetric(100, 1000000000000ull, buf, 0, 1000, '\0');
420     CHECK(!strcmp("100.00p", buf));
421   }
422 
423   SUBCASE("NegativePowersOfTen") {
424     auto nc_ = testing_notcurses();
425     if(!nc_){
426       return;
427     }
428     const wchar_t* smallsuffixes;
429     if(notcurses_canutf8(nc_)){
430       smallsuffixes = L"yzafpnµm";
431     }else{
432       smallsuffixes = L"yzafpnum";
433     }
434     char gold[NCPREFIXSTRLEN + 1];
435     char buf[NCPREFIXSTRLEN + 1];
436     uintmax_t goldval = 1;
437     uintmax_t val = 1;
438     size_t i = 0;
439     do{
440       REQUIRE(ncqprefix(val, 1000000000000000ull, buf, '\0'));
441       const int sidx = i / 3 + 3;
442       snprintf(gold, sizeof(gold), "%ju%s00%lc", goldval, decisep, smallsuffixes[sidx]);
443       CHECK(!strcmp(gold, buf));
444       if(UINTMAX_MAX / val < 10){
445         break;
446       }
447       val *= 10;
448       if((goldval *= 10) == 1000){
449         goldval = 1;
450       }
451     }while(++i < (wcslen(smallsuffixes) - 3) * 3);
452     // If we ran through all our suffixes, that's a problem
453     CHECK(wcslen(smallsuffixes) * 3 > i);
454     CHECK(0 == notcurses_stop(nc_));
455   }
456 
457   // inspired by #929
458   SUBCASE("BigMult") {
459     char qbuf[NCIPREFIXSTRLEN + 1];
460     CHECK(nullptr != impericize_ncmetric(1115614, 1000, qbuf, 0, 1000, '\0'));
461     CHECK(0 == strcmp("1.11K", qbuf));
462     CHECK(nullptr != impericize_ncmetric(372688, 1024, qbuf, 0, 1024, '\0'));
463     CHECK(0 == strcmp("363.95", qbuf));
464     CHECK(nullptr != impericize_ncmetric(372688, 1, qbuf, 0, 1024, '\0'));
465     CHECK(0 == strcmp("363.95K", qbuf));
466   }
467 
468 }
469