root/trunk/etc/bigint/radix.d

Revision 37, 11.1 kB (checked in by brad, 4 years ago)

incorporated Stewart Gordon's patches for radix and bigint_int

Line 
1 module etc.bigint.radix;
2 import etc.bigint.exception;
3 import etc.bigint.lowlevel;
4 private import std.utf;
5
6 /*
7
8 Copyright (c) 2004, Arcane Jill
9
10 All rights reserved. Intellectual Property Me Arse!
11
12 Redistribution and use in source and binary forms, with or without modification, are permitted
13 provided that the following conditions are met:
14
15    * Redistributions of source code must retain the above copyright notice, the phrase
16      "Intellectual Property Me Arse!", this list of conditions, and the following disclaimer.
17    * Redistributions in binary form must reproduce the above copyright notice, the phrase
18      "Intellectual Property Me Arse!", this list of conditions and the following disclaimer
19      in the documentation and/or other materials provided with the distribution.
20    * The name Arcane Jill may not be used to endorse or promote products derived from this
21      software without specific prior written permission.
22
23 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
24 OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
25 AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER,
26 OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
27 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
28 SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED, AND ON ANY
29 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
30 OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
31 OF SUCH DAMAGE.
32
33 */
34
35 private
36 {
37     struct RadixData
38     {
39         // These constants are pre-initialized for radix 10
40         uint a = 1000000000;    // = biggest power of 10 which will fit in a uint
41         uint n = 9;             // = log base 10 of the above
42         ulong m = 0x19999999;   // = (1<<32) / 10
43
44         // This function sets things up for any other arbitrary base
45         void initialize(uint radix)
46         {
47             m = 0x100000000;
48             ulong a1 = 1;
49             uint n1 = 0;
50             while (a1 < m)
51             {
52                 a = cast(uint) a1;
53                 n = n1;
54                 a1 *= radix;
55                 ++n1;
56             }
57             m /= radix;
58         }
59     }
60 }
61
62 /*  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
63     Construct a sting from a big integer array using default parameters
64 */
65 char[] bigintToString(uint[] x, uint radix, uint groupWidth)
66 in
67 {
68     assert(radix > 1 && radix <= 16);
69 }
70 body
71 {
72     dchar[] hex = "0123456789ABCDEF";
73     return toUTF8(bigintToString(x, radix, hex, groupWidth, '_'));
74 }
75
76 /*  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
77     Construct a sting from a big integer array
78 */
79 dchar[] bigintToString(uint[] x, uint radix, dchar[] digits, uint groupWidth, dchar groupChar)
80 in
81 {
82     assert(radix > 1);
83     assert(digits.length >= radix);
84 }
85 body
86 {
87     uint[] t = bigintToRadix(x, radix);
88     dchar[] d;
89     d.length = t.length;
90     for (int i=0; i<t.length; ++i)
91     {
92         d[i] = digits[t[i]];
93     }
94     t[] = 0;
95     if (groupWidth == 0)
96     {
97         return d.reverse;
98     }
99     else
100     {
101         dchar[] g;
102         g.length = 2 * d.length;
103         uint gx = 0;
104         uint ix = 0;
105         for (int i=0; i<d.length; ++i)
106         {
107             if (ix == groupWidth)
108             {
109                 g[gx++] = groupChar;
110                 ix = 0;
111             }
112             g[gx++] = d[i];
113             ++ix;
114         }
115         g.length = gx;
116         d[] = 0;
117         return g.reverse;
118     }
119 }
120
121 /*  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
122     Construct a big integer array from a string using default parameters
123 */
124 uint[] bigintFromString(char[] s, uint radix)
125 in
126 {
127     assert((radix == uint.max) || (radix > 1 && radix <= 16));
128 }
129 body
130 {
131     if (s.length == 0)
132     {
133         uint[] r;
134         r.length = 1;
135         return r;
136     }
137     if (radix == uint.max)
138     {
139         if (s.length > 2)
140         {
141             switch(s[0..2])
142             {
143                 case "0x":
144                 case "0X": s = s[2..s.length]; radix = 16; break;
145                 case "0d":
146                 case "0D": s = s[2..s.length]; radix = 10; break;
147                 case "0o":
148                 case "0O": s = s[2..s.length]; radix =  8; break;
149                 case "0b":
150                 case "0B": s = s[2..s.length]; radix =  2; break;
151                 default:
152                 {
153                     if (s[0] == '0') throw new IntException("Leading zero not permitted if radix unspecified");
154                     radix = 10;
155                 }
156             }
157         }
158         else (radix = 10);
159     }
160     dchar[] hex = "0123456789ABCDEF";
161     return bigintFromString(toUTF32(s), radix, hex, '_');
162 }
163
164 /*  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
165     Construct a big integer array from a string
166 */
167 uint[] bigintFromString(dchar[] s, uint radix, dchar[] digits, dchar groupChar)
168 in
169 {
170     assert(radix > 1);
171     assert(digits.length >= radix);
172 }
173 body
174 {
175     // Reverse the string and translate it into an array
176     uint[] x;
177     x.length = s.length;
178     uint xi = 0;
179     for (int si = s.length-1; si>=0; --si)
180     {
181         dchar c = s[si];
182         if (c == groupChar) continue;
183         x[xi++] = translateDigit(c, digits);
184     }
185
186     // Now convert it
187     x.length = xi;
188     uint[] r = bigintFromRadix(x, radix);
189     x[] = 0;
190     return r;
191 }
192
193 private
194 {
195     uint translateDigit(dchar c, dchar[] digits)
196     {
197         for (int i=0; i<digits.length; ++i)
198         {
199             if (digits[i] == c) return i;
200         }
201         throw new IntException("Invalid digit encountered during radix conversion");
202     }
203 }
204
205 /*  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
206     Construct an array of base radix digits from a big integer array
207 */
208 uint[] bigintToRadix(uint[] x, uint radix)
209 in
210 {
211     assert(radix > 1);
212 }
213 body
214 {
215     uint[] r;
216     x.length = bigintLLMinimize(x, x.length);
217     if (x.length == 0)
218     {
219         r.length = 1;
220     }
221     else
222     {
223         int shiftCount = log2Exact(radix);
224         if (shiftCount >= 0)
225         {
226             if (log2Exact(shiftCount) >= 0)
227             {
228                 r = bigintToRadixFastShift(x, shiftCount);
229             }
230             else
231             {
232                 r = bigintToRadixSlowShift(x, shiftCount);
233             }
234         }
235         else
236         {
237             r = bigintToRadixDivide(x, radix);
238         }
239         r.length = bigintLLMinimize(r, r.length);
240     }
241     return r;
242 }
243
244 private
245 {
246     uint[] bigintToRadixFastShift(uint[] x, uint shiftCount)
247     {
248         uint mask = (1 << shiftCount) - 1;
249         uint k = 32 / shiftCount;
250         uint[] r;
251         r.length = k * x.length;
252         uint rx = 0;
253         for (uint i=0; i<x.length; ++i)
254         {
255             uint n = x[i];
256             for (uint j=0; j<k; ++j)
257             {
258                 r[rx++] = n & mask;
259                 n >>= shiftCount;
260             }
261         }
262         return r;
263     }
264
265     uint[] bigintToRadixSlowShift(uint[] x_, uint shiftCount)
266     {
267         uint[] x;
268         uint len = x_.length;
269         x.length = len;
270         x[] = x_[];
271
272         uint mask = (1 << shiftCount) - 1;
273         uint k = 32 / shiftCount + 1;
274         uint[] r;
275         r.length = k * x.length;
276         uint rx = 0;
277         while (!bigintLLEqualsZero(x, len))
278         {
279             r[rx++] = x[0] & mask;
280             bigintLLShr(x, x, shiftCount, len);
281             if (x[len-1] == 0) --len;
282         }
283         return r;
284     }
285
286     uint[] bigintToRadixDivide(uint[] x_, uint radix)
287     {
288         // Make a copy of x to avoid corrupting the original
289         uint[] x;
290         uint len = x_.length;
291         x.length = len;
292         x[] = x_[];
293
294         // Figure out how many radix digits we can fit in a uint
295         RadixData rd;
296         if (radix != 10)
297         {
298             rd.initialize(radix);
299         }
300
301         // Now divide the number down into groups of digits
302         uint[] g;
303         g.length = len + len;
304         uint gx = 0;
305         while (!bigintLLEqualsZero(x, len))
306         {
307             g[gx++] = bigintLLDiv(x, x, rd.a, len);
308             if (x[len-1] == 0) --len;
309         }
310
311         // Finally split the groups of digits into single digits
312         uint[] r;
313         r.length = (rd.n + 1) * gx;
314         uint rx = 0;
315         for (int i=0; i<gx; ++i)
316         {
317             ulong gn = g[i];
318             for (int j=0; j<rd.n; ++j)
319             {
320                 ulong gq = gn * rd.m;
321                 gq >>= 32;
322                 ulong gr = gn - radix * gq;
323                 while (gr >= radix)
324                 {
325                     gr -= radix;
326                     ++gq;
327                 }
328                 r[rx++] = gr;
329                 gn = gq;
330             }
331         }
332         g[] = 0;
333         return r;
334     }
335 }
336
337 /*  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
338     Construct a big integer array from an array of base radix digits
339 */
340 uint[] bigintFromRadix(uint[] x, uint radix)
341 in
342 {
343     assert(radix > 1);
344 }
345 body
346 {
347     uint[] r;
348     x.length = bigintLLMinimize(x, x.length);
349     if (x.length == 0)
350     {
351         r.length = 1;
352     }
353     else
354     {
355         int shiftCount = log2Exact(radix);
356         if (shiftCount >= 0)
357         {
358             if (log2Exact(shiftCount) >= 0)
359             {
360                 r = bigintFromRadixFastShift(x, shiftCount);
361             }
362             else
363             {
364                 r = bigintFromRadixSlowShift(x, shiftCount);
365             }
366         }
367         else
368         {
369             r = bigintFromRadixMultiply(x, radix);
370         }
371         r.length = bigintLLMinimize(r, r.length);
372     }
373     return r;
374 }
375
376 private
377 {
378     uint[] bigintFromRadixFastShift(uint[] x, uint shiftCount)
379     {
380         debug uint mask = ~((1 << shiftCount) - 1);
381         uint k = 32 / shiftCount;
382         uint[] r;
383         r.length = x.length / k + 1;
384         uint rx = 0;
385         uint xx = 0;
386         uint n = 0;
387         for (uint i=0; i<x.length; ++i)
388         {
389             debug assert((x[i] & mask) == 0);
390             n >>= shiftCount;
391             n |= x[i] << (32-shiftCount);
392             ++xx;
393             if (xx == k)
394             {
395                 r[rx++] = n;
396                 n = xx = 0;
397             }
398         }
399         if (xx != 0)
400         {
401             r[rx++] = n >> (32 - xx*shiftCount);
402         }
403         r.length = rx;
404         return r;
405     }
406
407     uint[] bigintFromRadixSlowShift(uint[] x, uint shiftCount)
408     {
409         uint[] t = bigintFromRadixFastShift(x, shiftCount);
410         uint k = 32 % shiftCount;
411         for (uint i=t.length-1; i!=0; --i)
412         {
413             uint carry = bigintLLShr(&t[i], &t[i], k, t.length-i);
414             t[i-1] |=  carry;
415         }
416         return t;
417     }
418
419     uint[] bigintFromRadixMultiply(uint[] x, uint radix)
420     {
421         // Figure out how many radix digits we can fit in a uint
422         RadixData rd;
423         if (radix != 10)
424         {
425             rd.initialize(radix);
426         }
427
428         // Make room for the answer
429         uint[] r;
430         r.length = x.length / rd.n + 1;
431
432         // Now calculate the answer
433         uint p = x.length % rd.n;
434         uint n = 0;
435         int i = x.length;
436         int j;
437         for (j=p-1; j>=0; --j)
438         {
439             n *= radix;
440             n += x[--i];
441             assert(x[i] < radix);
442         }
443         r[0] = n;
444         while (i>0)
445         {
446             n = 0;
447             for (j=rd.n-1; j>=0; --j)
448             {
449                 n *= radix;
450                 n += x[--i];
451                 assert(x[i] < radix);
452             }
453             bigintLLMul(r, r, rd.a, r.length);
454             bigintLLInc(r, r, n, r.length);
455         }
456         return r;
457     }
458
459 }
460
461 /*  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
462     Get the value n for which (1<<n) == x, or -1 if this is not possible
463 */
464 int log2Exact(uint x)
465 {
466     switch (x)
467     {
468         case 0x00000001: return  0;
469         case 0x00000002: return  1;
470         case 0x00000004: return  2;
471         case 0x00000008: return  3;
472         case 0x00000010: return  4;
473         case 0x00000020: return  5;
474         case 0x00000040: return  6;
475         case 0x00000080: return  7;
476         case 0x00000100: return  8;
477         case 0x00000200: return  9;
478         case 0x00000400: return 10;
479         case 0x00000800: return 11;
480         case 0x00001000: return 12;
481         case 0x00002000: return 13;
482         case 0x00004000: return 14;
483         case 0x00008000: return 15;
484         case 0x00010000: return 16;
485         case 0x00020000: return 17;
486         case 0x00040000: return 18;
487         case 0x00080000: return 19;
488         case 0x00100000: return 20;
489         case 0x00200000: return 21;
490         case 0x00400000: return 22;
491         case 0x00800000: return 23;
492         case 0x01000000: return 24;
493         case 0x02000000: return 25;
494         case 0x04000000: return 26;
495         case 0x08000000: return 27;
496         case 0x10000000: return 28;
497         case 0x20000000: return 29;
498         case 0x40000000: return 30;
499         case 0x80000000: return 31;
500         default:         return -1;
501     }
502 }
Note: See TracBrowser for help on using the browser.