1 // Written in the D programming language
2 
3 /**
4  * Rounding methods for floating-point decimal arithmetic.
5  *
6  * Authors: Paul D. Anderson
7  *
8  * Copyright: Copyright 2009-2017 by Paul D. Anderson.
9  *
10  * License: <a href="http://www.boost.org/LICENSE_1_0.txt">Boost License 1.0</a>
11  *
12  * Standards:
13  *  General Decimal Arithmetic Specification,
14  *  Version 1.70, (25 March 2009).
15  */
16 
17 module eris.decimal.rounding;
18 
19 import eris.decimal;
20 
21 import std.conv;
22 import std..string;
23 import std.traits;
24 import std.stdio;
25 
26 unittest {
27   writeln("    rounding tests    ");
28   writeln("==========================");
29 }
30 
31 version(unittest)
32 {
33   import std.stdio;
34   import eris.decimal.test;
35 }
36 
37 /**
38  *  Returns the number rounded to its type precision.
39  *  Flags: SUBNORMAL, CLAMPED, OVERFLOW, INEXACT, ROUNDED.
40  */
41 public D round(D)(in D num) if (isDecimal!D)
42 {
43   return round(num, D.context);
44 }
45 
46 /**
47  *  Rounds the number to the precision of the context parameter.
48  *  if setFlags is false no context flags will be set by this operation.
49  *  Returns the rounded number.
50  *  Flags: SUBNORMAL, CLAMPED, OVERFLOW, INEXACT, ROUNDED.
51  */
52 package D round(D)(in D num, Context context = D.context, bool setFlags = true)
53   if (isDecimal!D)
54 {
55   return round(num,
56     context.precision, context.mode, setFlags);
57 }
58 
59 /**
60  *  Rounds the number to the specified precision using the specified rounding mode.
61  *  if setFlags is false none of the context flags will be set by this operation.
62  *  Flags: SUBNORMAL, CLAMPED, OVERFLOW, INEXACT, ROUNDED.
63  */
64 //@safe
65 package D round(D)(in D num, int precision, Round mode = D.mode,
66     bool setFlags = true)
67   if (isDecimal!D)
68 {
69   D copy = num;
70   // check for overflow before rounding and copy the input
71   copy = overflow(num, mode, setFlags);
72 
73   // check rounding mode
74   if (mode == ROUND_NONE) return copy;
75 
76   // special values aren't rounded
77   if (!copy.isFinite) return copy;
78 
79   // handle subnormal numbers
80   if (!copy.isZero && copy.isSubnormal())
81   {
82     if (setFlags) contextFlags.set(SUBNORMAL);
83 //    int diff = copy.adjExpo - copy.tinyExpo;
84     int subprecision = copy.adjExpo - copy.tinyExpo + 1;
85     // use the subnormal precision and round
86     if (precision < subprecision) subprecision = precision;
87     if (copy.digits > subprecision) {
88       copy = modeRound(copy, subprecision, mode, setFlags);
89     }
90     // if the result of rounding a subnormal is zero
91     // the clamped flag is set. (Spec. p. 51)
92     if (copy.adjExpo < copy.tinyExpo)
93     {
94       copy.expo = copy.tinyExpo;
95       if (setFlags) contextFlags.set(CLAMPED);
96     }
97     return copy;
98   }
99   // zero values aren't rounded, but they are checked for
100   // subnormal exponents.
101   if (copy.isZero)
102   {
103     if (copy.expo < copy.minExpo)
104     {
105       if (setFlags) contextFlags.set(SUBNORMAL);
106       if (copy.expo < copy.tinyExpo)
107       {
108         copy.expo = copy.tinyExpo;
109       }
110     }
111     return copy;
112   }
113 
114 
115   // round the number
116   return modeRound(copy, precision, mode, setFlags);
117 
118 } // end round()
119 
120 unittest
121 {  // round
122   static struct S { D16 x; int n; D16 expect; }
123   S[] s =
124   [
125     { "9999", 3, "1.00E+4" },
126     { "1234567890", 3, "1.23E+9" },
127     { "1234567890", 4, "1.235E+9" },
128     { "1234567890", 5, "1.2346E+9" },
129     { "1234567890", 6, "1.23457E+9" },
130     { "1234567890", 7, "1.234568E+9" },
131     { "1234567890", 8, "1.2345679E+9" },
132     { "1235",  3, "1.24E+3" },
133     { "12359", 3, "1.24E+4" },
134     { "1245",  3, "1.24E+3" },
135     { "12459", 3, "1.25E+4" },
136     // subnormal numbers
137     { "1.234567890123456E-370", 16, "1.23456789012346E-370" },
138     { "1.234567890123456E-380", 12, "1.2346E-380" },
139     { "1.234567890123456E-381", 9,  "1.235E-381" },
140     { "1.234567890123456E-381", 3,  "1.23E-381" },
141     { "1.234567890123456E-382", 6,  "1.23E-382" },
142     { "1.234567899123456E-383", 3,  "1.2E-383" },
143     { "1.234567899123456E-384", 6,  "1E-384" },
144     { "1E-369", 3, "1E-369" },
145     { "1E-384", 3, "1E-384" },
146     { "1E-385", 3, "0E-384" },
147     { "0.1E-384", 3, "0E-384" },
148     { "1234E-385", 3, "1.23E-382" },
149     // extreme range zeros
150     { "0E+369", 3, "0E-368" },
151     { "0E+370", 3, "0E-368" },
152     // TODO: I don't think overrange zeros should have tinyExpo
153     { "0E-368", 3, "0E-368" },
154     { "0E-369", 3, "0E-369" },
155     { "0E-384", 3, "0E-384" },
156     { "0E-385", 3, "0E-384" },
157   ];
158   auto f = FunctionTest!(S,D16)("roundPrec");
159   foreach (t; s) f.test(t, round!D16(t.x,t.n));
160   writefln(f.report);
161 }
162 
163 //--------------------------------
164 // private methods
165 //--------------------------------
166 
167 // TODO: needs unittests
168 // TODO: return boolean value
169 /**
170  *  Returns true if the number is too large to be represented
171  *  and adjusts the number according to the rounding mode.
172  *  Implements the 'overflow' processing in the specification. (p. 53)
173  *  Flags: OVERFLOW, ROUNDED, INEXACT.
174  *  Precondition: number must be finite.
175  */
176 //@safe
177 private D overflow(D)(in D num, Round mode = D.mode,
178     bool setFlags = true)
179   if (isDecimal!D)
180 {
181   if (!num.isFinite) return num;
182   if (num.adjExpo <= D.maxExpo) return num;
183 
184   if (setFlags) contextFlags.set(OVERFLOW | INEXACT | ROUNDED);
185 
186   switch (mode)
187   {
188   case HALF_UP:
189   case HALF_EVEN:
190   case HALF_DOWN:
191   case ROUND_UP:
192     return D.infinity(num.sign);
193   case ROUND_DOWN:
194     return num.sign ? D.max.copyNegate : D.max;
195   case CEILING:
196     return num.sign ? D.max.copyNegate : D.infinity;
197   case FLOOR:
198     return num.sign ? D.infinity(true) : D.max;
199   default:
200     return num;
201   }
202 }
203 
204 // TODO: these tests aren't very useful
205 unittest
206 {  // overflow
207   static struct S { D16 num; Round mode; D16 expect; }
208   S[] s =
209   [
210     { "1000",  HALF_EVEN,  "1000" },
211     { "1000000", HALF_EVEN,  "10000E+2" },
212     { "99999",   HALF_EVEN,  "99999" },
213 //    { "1234550", HALF_EVEN,  "1.2346E+6" },
214 //    { "1234550", ROUND_DOWN, "1.2345E+6" },
215 //    { "1234550", ROUND_UP,   "1.2346E+6" },
216   ];
217   auto f = FunctionTest!(S,D16)("overflow");
218   foreach (t; s) f.test(t, overflow!D16(t.num,t.mode));
219   writefln(f.report);
220 }
221 
222 /**
223  *  Rounds the number to the specified precision
224  *  using the specified rounding mode.
225  */
226 private D modeRound(D)(D num, int precision, Round mode = D.mode,
227     bool setFlags = true)
228   if (isDecimal!D)
229 {
230   // finite numbers only
231   if (!num.isFinite) return num;
232   // check rounding mode
233   if (mode == ROUND_NONE) return num;
234   // if the number is short enough, don't round
235   if (num.digits <= precision) return num;
236 
237   D copy = overflow(num, mode, setFlags);
238   // did it overflow ?
239   if (!copy.isFinite) return copy;
240 
241   // calculate the remainder
242   D remainder = getRemainder(copy, precision);
243   // if the number wasn't rounded, return
244   if (remainder.isZero) return copy;
245 
246   // check for deleted leading zeros in the remainder.
247   // makes a difference only in round-half modes.
248   if (mode == HALF_EVEN || mode == HALF_UP || mode == HALF_DOWN)
249   {
250     if (countDigits(remainder.coff) != remainder.digits)
251     {
252       return copy;
253     }
254   }
255 
256   switch (mode) {
257   case ROUND_UP:
258     incrementAndRound(copy);
259     break;
260   case ROUND_DOWN:
261     break;
262   case CEILING:
263     if (!num.sign) incrementAndRound(copy);
264     break;
265   case FLOOR:
266     if (num.sign) incrementAndRound(copy);
267     break;
268   case HALF_UP:
269     if (firstDigit(remainder.coff) >= 5)
270       incrementAndRound(copy);
271     break;
272   case HALF_DOWN:
273     if (testFive(remainder.coff) > 0)
274       incrementAndRound(copy);
275     break;
276   case HALF_EVEN:
277     switch (testFive(remainder.coff))
278     {
279     case -1:
280       break;
281     case 1:
282       incrementAndRound(copy);
283       break;
284     case 0:
285       if (lastDigit(copy.coff) & 1) {
286         incrementAndRound(copy);
287       }
288       break;
289     default:
290       break;
291     }
292     break;
293   default:
294     break;
295   }  // end switch (mode)
296   return overflow(copy, mode, setFlags);
297 }  // end modeRound()
298 
299 ///
300 unittest
301 {
302 }
303 
304 unittest
305 {  // modeRound
306   static struct S { D16 x; int p; Round r; D16 expect; }
307   S[] s =
308   [
309     { "1000",  5, HALF_EVEN,  "1000" },
310     { "1000000", 5, HALF_EVEN,  "10000E+2" },
311     { "99999",   5, HALF_EVEN,  "99999" },
312     { "1234550", 5, ROUND_NONE, "1234550" },
313     { "1234550", 5, ROUND_UP,   "1.2346E+6" },
314     { "1234550", 5, ROUND_DOWN, "1.2345E+6" },
315     { "1234550", 5, HALF_UP,  "1.2346E+6" },
316     { "1234550", 5, HALF_EVEN,  "1.2346E+6" },
317     { "1234550", 5, HALF_DOWN,  "1.2345E+6" },
318     { "1234550", 5, FLOOR,    "1.2345E+6" },
319     { "1234550", 5, CEILING,  "1.2346E+6" },
320   ];
321   auto f = FunctionTest!(S,D16)("modeRound");
322   foreach (t; s) f.test(t, modeRound!D16(t.x,t.p,t.r));
323   writefln(f.report);
324 }
325 
326 /**
327  *  Shortens the coefficient of the number to the specified precision,
328  *  adjusts the exponent, and returns the (unsigned) remainder.
329  *  If the number is already less than or equal to the precision, the
330  *  number is unchanged and the remainder is zero.
331  *  Otherwise the rounded flag is set, and if the remainder is not zero
332  *  the inexact flag is also set.
333  *  Flags: ROUNDED, INEXACT.
334  */
335 private D getRemainder(D) (ref D num, int precision) if (isDecimal!D)
336 {
337   int diff = num.digits - precision;
338   if (diff <= 0) return D.zero;
339 
340   D remainder = D.zero;
341   contextFlags.set(ROUNDED);
342   BigInt divisor = pow10b(diff);
343   BigInt dividend = num.coff;
344   BigInt quotient = dividend/divisor;
345   auto cf = dividend - quotient*divisor;
346   if (cf != 0) {
347   remainder.digits = diff;
348   remainder.expo = num.expo;
349   remainder.coff = cf;
350   contextFlags.set(INEXACT);
351   }
352   num.coff = quotient;
353   num.digits = countDigits(quotient); //precision;
354   num.expo = num.expo + diff;
355   return remainder;
356 }
357 
358 unittest
359 {  // getRemainder
360   static struct S { D16 x; int p; D16 expect; }
361   S[] s =
362   [
363   { 1234567890123456L, 5, "67890123456" },
364   ];
365   auto f = FunctionTest!(S,D16)("remainder");
366   foreach (t; s) f.test(t, getRemainder!D16(t.x,t.p));
367    writefln(f.report);
368 }
369 
370 /**
371  *  Increments the coefficient by one.
372  *  If this causes an overflow the coefficient is adjusted by clipping
373  *  the last digit (it will be zero) and incrementing the exponent.
374  */
375 private void incrementAndRound(D)(ref D num) if (isDecimal!D)
376 {
377   // if num is zero
378   if (num.digits == 0) {
379     num.coff = 1;
380     num.digits = 1;
381     return;
382   }
383   num.coff = num.coff + 1;
384   // TODO: (efficiency) is there a less expensive test?
385   if (lastDigit(num.coff) == 0) {
386     if (num.coff / pow10b(num.digits) > 0) {
387       num.coff = num.coff / 10;
388       num.expo = num.expo + 1;
389     }
390   }
391 }
392 
393 unittest
394 {  // incrementAndRound
395   D16 incr(D16 num)
396   {
397     incrementAndRound(num);
398     return num;
399   }
400 
401   static struct S { D16 num; D16 expect; }
402   S[] s =
403   [
404     {  10, 11 },
405     {  19, 20 },
406     { 999, "1.00E+3" },
407   ];
408   auto f = FunctionTest!(S,D16)("increment");
409   foreach (t; s) f.test(t, incr(t.num));
410   writefln(f.report);
411 }
412 
413 /*unittest {  // increment
414   write("-- increment&round..");
415   D16 actual, expect;
416   actual = 10;
417   expect = 11;
418   incrementAndRound(actual);
419   assertEqual(actual, expect);
420   actual = 19;
421   expect = 20;
422   incrementAndRound(actual);
423   assertEqual(actual, expect);
424   actual = 999;
425   expect = "1.00E+3";
426   incrementAndRound(actual);
427   assertEqual(actual, expect);
428   writeln("passed");
429 }*/
430 
431 ///
432 /// Returns a numeric string with the specified precision.
433 public string roundStr(string str, int precision)
434 {
435 
436   // make a copy, deleting any whitespace at ends of the string
437   char[] copy = strip(str).dup;
438 
439   // if the string has a decimal point increment the precision to account
440   // for the extra character
441   if (copy.indexOf('.') >= 0) precision++;
442 
443   // strip out any underscores
444   copy = copy.replace("_", "");
445 
446   // ignore leading zeros
447   size_t index = 0;
448   while (copy[index] == '0') index++;
449   precision += index;
450 
451   // if the precision is greater than the length return the whole string
452   if (precision >= copy.length) return str;
453 
454   // get the last digit in the (to-be-)clipped string,
455   // and the following digit
456   char last = str[precision-1];
457   char follow = str[precision];
458 
459   // if the following digit is less than five return the clipped string
460   if (follow < '5') return copy[0..precision].idup;
461 
462   // otherwise, increment last digit in the string(round half-up)
463   copy = copy[0..precision];
464   copy[precision-1] += 1;
465 
466   // if the increment resulted in a digit return the clipped string
467   if (copy[precision-1] <= '9') return copy[0..precision].idup;
468 
469   // otherwise, (the last digit increment resulted in a non-digit)
470   // set the last digit to '0' and increment its preceding digit,
471   // repeat as necessary
472   int lix = precision - 1;
473   last = copy[lix];
474   while (last > '9') {
475     copy[lix] = '0';
476     lix--;
477     copy[lix] += 1;
478     last = copy[lix];
479   }
480   return copy[0..precision].idup;
481 }
482 
483 /**
484  *  Returns -1, 1, or 0 if the remainder is less than, more than,
485  *  or exactly half the least significant digit of the shortened coefficient.
486  *  Exactly half is a five followed by zero or more zero digits.
487  */
488 private int testFive(in BigInt x)
489 {
490   int num = countDigits(x);
491   BigInt btens = pow10b(num - 1);
492   int first = cast(int)(x / btens);
493   if (first < 5) return -1;
494   if (first > 5) return +1;
495   BigInt zeros = x % btens;
496   return zeros ? 1 : 0;
497 }
498 
499 unittest
500 {  // testFive
501   static struct S { BigInt x; int expect; }
502   S[] s =
503   [
504     { "5000000000000000000000",  0 },
505     { "4999999999999999999999", -1 },
506     { "50000000000000000000000000000000000000000000000001", 1 },
507   ];
508   auto f = FunctionTest!(S,int)("testFive");
509   foreach (t; s) f.test(t, testFive(t.x));
510   writefln(f.report);
511 }
512 
513 //-----------------------------
514 // useful constants
515 //-----------------------------
516 
517 /*private enum BigInt BIG_ZERO = BigInt(0);
518 private enum BigInt BIG_ONE  = BigInt(1);
519 private enum BigInt BIG_FIVE = BigInt(5);
520 private enum BigInt BIG_TEN  = BigInt(10);
521 private enum BigInt BILLION  = BigInt(1_000_000_000);
522 private enum BigInt QUINTILLION  = BigInt(1_000_000_000_000_000_000);*/
523 
524 
525 ///  The maximum number of decimal digits that fit in an int value.
526 //public enum int MAX_INT_DIGITS = 9;
527 ///  The maximum decimal value that fits in an int.
528 //public enum uint MAX_DECIMAL_INT = 999999999U;
529 ///  The maximum number of decimal digits that fit in a long value.
530 //public enum int MAX_LONG_DIGITS = 18;
531 ///  The maximum decimal value that fits in a long value.
532 //public enum ulong MAX_DECIMAL_LONG = 10UL^^MAX_LONG_DIGITS - 1;
533 
534 //-----------------------------
535 // BigInt digit functions
536 //-----------------------------
537 
538 private enum ten = 10UL;
539 /**
540  *  An array of unsigned long integers with values of
541  *  powers of ten from 10^^0 to 10^^19
542  */
543 private enum ulong[20] pow10UL = [ten^^0,
544     ten^^1,  ten^^2,  ten^^3,  ten^^4,  ten^^5,  ten^^6,
545     ten^^7,  ten^^8,  ten^^9,  ten^^10, ten^^11, ten^^12,
546     ten^^13, ten^^14, ten^^15, ten^^16, ten^^17, ten^^18, ten^^19];
547 
548 /**
549  * Returns the number of decimal digits in a non-negative big integer
550  */
551 public int countDigits(T)(in T m)
552   if (is(T == BigInt) || isUnsigned!T)
553 {
554   if (m == 0)
555   {
556     return 0;
557   }
558 
559   static if (is(T == BigInt))
560   {
561     if (m.ulongLength == 1)
562     {
563       return countDigits(m.getDigit(0));
564     }
565 
566     int count;
567     ulong n = clipDigits(m, count);
568     return count + countDigits(n);
569   }
570   else
571   {
572     auto n = cast(ulong)m;
573     // special cases:
574     if (n == 0) return 0;
575     if (n < 10) return 1;
576     if (n >= pow10UL[19]) return 20;
577     // use a binary search to count the digits
578     int min = 2;
579     int max = 19;
580     while (min <= max)
581     {
582       int mid = (min + max)/2;
583       if (n < pow10UL[mid])
584       {
585         max = mid - 1;
586       }
587       else
588       {
589         min = mid + 1;
590       }
591     }
592     return min;
593   }
594 }
595 
596 ///
597 unittest // countDigits
598 {
599   auto big = BigInt(
600     "1234567890_1234567890_1234567890_1234567890_1234567890" ~
601     "1234567890_1234567890_1234567890_1234567890_1234567890_1");
602   assert(countDigits(big) == 101);
603 }
604 
605 unittest
606 {  // countDigits
607   static struct S { BigInt n; int expect; }
608   S[] s =
609   [
610     { "123456", 6 },
611     { "12345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901", 101 },
612     { "1234567890123456789012345678901234567890123456789012", 52 },
613   ];
614   auto f = FunctionTest!(S,int)("countDigits");
615   foreach (t; s) f.test(t, countDigits(t.n));
616   writefln(f.report);
617 }
618 
619 unittest
620 {  // countDigits
621   static struct S { ulong n; int expect; }
622   S[] s =
623   [
624     {          7,  1 },
625     {         13,  2 },
626     {        999,  3 },
627     {         9999,  4 },
628     {        25978,  5 },
629     {      2008617,  7 },
630     {     1234567890, 10 },
631     {    10000000000, 11 },
632     {  123456789012345, 15 },
633     {  1234567890123456, 16 },
634     {123456789012345678, 18 },
635     {       long.max, 19 },
636     {      ulong.max, 20 },
637     {      ulong.min,  0 },
638   ];
639   auto f = FunctionTest!(S,int)("countDigits");
640   foreach (t; s) f.test(t, countDigits(t.n));
641   writefln(f.report);
642 }
643 
644 
645 /// Clips decimal digits until a single ulong digit is left.
646 private ulong clipDigits(in BigInt big, out int count)
647 {
648   if (big <= ulong.max) return cast(ulong)big;
649 
650   enum BigInt tenQuint = 10UL^^19;
651   count = 0;
652   BigInt quotient = big;
653   while (quotient > tenQuint) {
654     quotient /= tenQuint;
655     count += 19;
656   }
657   return cast(ulong)quotient;
658 }
659 
660 unittest // clipDigits
661 {
662   int count;
663   BigInt big;
664   big = BigInt("123456");
665   assert(clipDigits(big, count) == 123456);
666   assert(clipDigits(BigInt(ulong.max), count) == ulong.max);
667   big = BigInt("1234567890_1234567890_1234567890_1234567890_1234567890");
668   assert(clipDigits(big, count) == 123456789012);
669 }
670 
671 /// returns the first decimal digit of the number
672 public uint firstDigit(T)(T n)
673   if (is(T == BigInt) || isUnsigned!T)
674 {
675   if (n == 0) return 0;
676   if (n < 10) return cast(int) n;
677   static if (is(T == BigInt))
678   {
679     int count;
680     return firstDigit(clipDigits(n,count));
681   }
682   else
683   {
684     int digits = countDigits(n);
685     return cast(uint)(n / pow10UL[digits-1]);
686   }
687 }
688 
689 ///
690 unittest
691 {
692   assert(firstDigit(BigInt("8234567890123456789012345678901234567890123")) == 8);
693   assert(firstDigit(0U) == 0);
694   assert(firstDigit(7U) == 7);
695   assert(firstDigit(9999UL) == 9);
696   assert(firstDigit(uint.max) == 4);
697   assert(firstDigit(ulong.max) == 1);
698 }
699 
700 unittest  // move to regression testing
701 {  // firstDigit(BigInt)
702   static struct S { BigInt n; uint expect; }
703   S[] s =
704   [
705     { "5000000000000000000000", 5 },
706     { "45500000001209854300023400000000000000", 4 },
707     { "82345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678905", 8 },
708   ];
709   auto f = FunctionTest!(S,uint)("1stDigit(b)");
710   foreach (t; s) f.test(t, firstDigit(t.n));
711   writefln(f.report);
712 }
713 
714 unittest
715 {  // firstDigit(ulong)
716   static struct S { ulong n; uint expect; }
717   S[] s =
718   [
719     { 0, 0 },
720     { 7, 7 },
721     { 13, 1 },
722     { 999, 9 },
723     { 9999, 9 },
724     { 25987, 2 },
725     { 5008617, 5 },
726     { 3234567890, 3 },
727     { 10000000000, 1 },
728     { 823456789012345, 8 },
729     { 4234567890123456, 4 },
730     { 623456789012345678, 6 },
731     {long.max, 9 }
732   ];
733   auto f = FunctionTest!(S,uint)("1stDigit(u)");
734   foreach (t; s) f.test(t, firstDigit(t.n));
735   writefln(f.report);
736 }
737 
738 /**
739  *  Decimal shift left.
740  *  Shifts the number left by the specified number of decimal digits.
741  *  If n == 0 the number is returned unchanged.
742  *  If n < 0  the number is shifted right.
743  */
744 public BigInt shiftBig(BigInt num, int n)
745 {
746   if (n > 0) {
747     num *= pow10b(n);
748   }
749   if (n < 0) {
750     num /= pow10b(-n);
751   }
752   return num;
753 }
754 
755 ///
756 unittest {  // shiftBig(BigInt)
757   BigInt big;
758   int n;
759   big = 12345;
760   n = 2;
761   assert(shiftBig(big, n) == 1234500);
762   big = 1234567890;
763   n = 7;
764   assert(shiftBig(big, n) == BigInt(12345678900000000));
765   big = 12;
766   n = 2;
767   assert(shiftBig(big, n) == 1200);
768   big = 12;
769   n = 4;
770   assert(shiftBig(big, n) == 120000);
771   BigInt res;
772   big = BigInt("9223372036854775807");
773   n = -10;
774   assert(shiftBig(big, n) == BigInt("922337203"));
775   big = BigInt("9223372036854775808");
776   n = -10;
777   assert(shiftBig(big, n) == BigInt("922337203"));
778 }
779 
780 ///  Returns the last digit of the argument.
781 public int lastDigit(in BigInt big)
782 {
783   auto digit = big % BigInt(10);
784   if (digit < 0) digit = -digit;
785   return digit.toInt;
786 }
787 
788 unittest
789 {  // lastDigit
790   static struct S { BigInt n; int expect; }
791   S[] s =
792   [
793     {  7, 7 },
794     { -13, 3 },
795     {  999, 9 },
796     { -9999, 9 },
797     {  25987, 7 },
798     { -5008615, 5 },
799     {  3234567893, 3 },
800     { -10000000000, 0 },
801     {  823456789012348, 8 },
802     {  4234567890123456, 6 },
803     {  623456789012345674, 4 },
804     {  long.max, 7 },
805   ];
806   auto f = FunctionTest!(S,int)("lastDigit");
807   foreach (t; s) f.test(t, lastDigit(t.n));
808   writefln(f.report);
809 }
810 
811 ///  Returns the number of trailing zeros in the argument.
812 public uint trailingZeros(BigInt n, uint digits)
813 {
814   // shortcuts for frequent values
815   if (n ==  0) return 0;
816   if (n %  10) return 0;
817   if (n % 100) return 1;
818   // find by binary search
819   uint min = 3;
820   uint max = digits - 1;
821   while (min <= max) {
822     int mid = (min + max)/2;
823     if (n % pow10b(mid) != 0)
824     {
825       max = mid - 1;
826     }
827     else
828     {
829       min = mid + 1;
830     }
831   }
832   return max;
833 }
834 ///
835 unittest
836 {
837   auto big = BigInt("1234567890123456789012300000000000");
838   auto digits = countDigits(big);
839   assert(digits == 34);
840   auto zeros = trailingZeros(big, digits);
841   assert(zeros == 11);
842 }
843 
844 
845 ///  Trims any trailing zeros and returns the number of zeros trimmed.
846 public int clipZeros(ref BigInt n, uint digits)
847 {
848   if (digits <= 0) return 0;
849   auto zeros = trailingZeros(n, digits);
850   if (zeros == 0) return 0;
851   n /= pow10b(zeros);
852   return zeros;
853 }
854 
855 ///
856 unittest {
857   auto big = BigInt("123456789000000");
858   auto digits = countDigits(big);
859   assert(digits == 15);
860   auto zeros = clipZeros(big, digits);
861   assert(zeros == 6);
862 }
863 
864 unittest {
865   writeln("==========================");
866 }
867