1 module bc..string.numeric;
2 
3 import bc.core.intrinsics;
4 import std.traits : isIntegral, isSigned;
5 
6 pure @safe nothrow @nogc:
7 
8 /// Calculates number of digits of the provided number including sign character for negative numbers.
9 int numDigits(T)(T number)
10     if (isIntegral!T)
11 {
12     pragma(inline);
13 
14     // handle negative numbers
15     static if (isSigned!T)
16     {
17         if (_expect(number == long.min, false)) return 20; // for safety as it can't be converted to positive number
18         if (number < 0) return 1 + numDigits(-long(number));
19     }
20 
21     if (__ctfe)
22     {
23         // simple ctfe version
24         import std.traits : Unqual;
25         if (_expect(!number, false)) return 1; // special case for 0
26         int digits = 0;
27         alias UT = Unqual!T;
28         UT n = number;
29         while (n) { n /= 10; digits++; }
30         return digits;
31     }
32     else
33     {
34         // specialization for 1B numbers
35         static if (T.sizeof == 1)
36         {
37             static immutable ubyte[256] dmap = () {
38                 ubyte[256] ret;
39                 for (int i=0; i < 256; ++i) ret[i] = cast(ubyte)numDigits(i);
40                 return ret;
41             }();
42             return dmap[number];
43         }
44         else
45         {
46             if (_expect(number >= 10_000, false)) {
47                 if (number >= 10_000_000) {
48                     if (number >= 100_000_000) {
49                         if (number >= 1_000_000_000) {
50                             static if (T.sizeof <= 4) return 10;
51                             else {
52                                 // 8bit number branch
53                                 if (number >= 10_000_000_000) {
54                                     if (number >= 100_000_000_000) {
55                                         if (number >= 1_000_000_000_000) {
56                                             if (number >= 10_000_000_000_000) {
57                                                 if (number >= 100_000_000_000_000) {
58                                                     if (number >= 1_000_000_000_000_000) {
59                                                         if (number >= 10_000_000_000_000_000) {
60                                                             if (number >= 100_000_000_000_000_000) {
61                                                                 if (number >= 1_000_000_000_000_000_000) {
62                                                                     if (number >= 10_000_000_000_000_000_000UL)
63                                                                         return 20;
64                                                                     return 19;
65                                                                 }
66                                                                 return 18;
67                                                             }
68                                                             return 17;
69                                                         }
70                                                         return 16;
71                                                     }
72                                                     return 15;
73                                                 }
74                                                 return 14;
75                                             }
76                                             return 13;
77                                         }
78                                         return 12;
79                                     }
80                                     return 11;
81                                 }
82                                 return 10;
83                             }
84                         }
85                         return 9;
86                     }
87                     return 8;
88                 }
89                 if (number >= 100_000) {
90                     if (number >= 1_000_000) return 7;
91                     return 6;
92                 }
93                 return 5;
94             }
95             if (number >= 100) {
96                 if (number >= 1_000) return 4;
97                 return 3;
98             }
99             if (number >= 10) return 2;
100             return 1;
101         }
102     }
103 }
104 
105 ///
106 @("numDigits CT")
107 @safe unittest
108 {
109     // ctfe tests
110     static assert(numDigits(0) == 1);
111     static assert(numDigits(11) == 2);
112     static assert(numDigits(-1) == 2);
113 
114     static assert(numDigits(int.min) == 11);
115     static assert(numDigits(int.max) == 10);
116     static assert(numDigits(long.min) == 20);
117     static assert(numDigits(long.max) == 19);
118     static assert(numDigits(ulong.min) == 1);
119     static assert(numDigits(ulong.max) == 20);
120 }
121 
122 ///
123 @("numDigits RT")
124 @safe unittest
125 {
126     // uint.max = 4_294_967_295 -> 10 digits
127     // ulong.max = 18_446_744_073_709_551_615 -> 20 digits
128     // long max = 9_223_372_036_854_775_807 -> 19 digits
129 
130     // rt tests
131     assert(numDigits(0) == 1);
132     assert(numDigits(11) == 2);
133     assert(numDigits(-1) == 2);
134     assert(numDigits(-123) == 4);
135 
136     assert(numDigits(int.min) == 11);
137     assert(numDigits(int.max) == 10);
138     assert(numDigits(long.min) == 20);
139     assert(numDigits(long.max) == 19);
140     assert(numDigits(ulong.min) == 1);
141     assert(numDigits(ulong.max) == 20);
142 
143     long n = 10;
144     foreach (i; 0..20)
145     {
146         import std.math : pow;
147         assert(numDigits(pow(10UL, i)) == i+1);
148     }
149 }