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