1 /* This file is part of the Amalthea library.
2  *
3  * Copyright (C) 2020-2021 Eugene 'Vindex' Stulin
4  *
5  * Distributed under the Boost Software License 1.0 or (at your option)
6  * the GNU Lesser General Public License 3.0 or later.
7  */
8 
9 module amalthea.decimal;
10 
11 public import amalthea.libcore;
12 import amalthea.dataprocessing : makeFilledArray;
13 
14 import std.array, std.algorithm, std.bigint, std.format, std.math, std.string;
15 
16 private size_t defaultFixedPoint = 18;
17 
18 
19 /*******************************************************************************
20  * Sets default value of fixed point positon for all new Decimal structures.
21  */
22 void setDefaultFixedPoint(size_t fixedPoint) {
23     defaultFixedPoint = fixedPoint;
24 }
25 
26 
27 /*******************************************************************************
28  * Gets current value of fixed point position by default.
29  * If the value was not changed by setDefaultFixedPoint, the value is 18.
30  */
31 size_t getDefaultFixedPoint() {
32     return defaultFixedPoint;
33 }
34 
35 
36 /*******************************************************************************
37  * Transforms number or string to Decimal.
38  */
39 Decimal toDecimal(T)(T value) {
40     return Decimal(value);
41 }
42 
43 
44 /// Short alias for main structure.
45 alias D = Decimal;
46 
47 
48 /*******************************************************************************
49  * A struct representing a decimal fixed-point number.
50  *
51  * By default, the number of digits after the decimal point is 18.
52  * The decimal point position is limited
53  * by the maximum value of the size_t data type.
54  * Decimal is based on std.bigint.BigInt and have no limitation
55  * for the number of significant digits.
56  */
57 struct Decimal {
58     private BigInt value;
59     private size_t fixedPoint;
60 
61     /***************************************************************************
62      * Constucts an object from a decimal string.
63      *
64      * Params:
65      *     number = Decimal string with comma/point or without it.
66      *     fixedPointPosition = Position of decimal point in new object.
67      */
68     this(string number, size_t fixedPointPosition = defaultFixedPoint) {
69         fixedPoint = fixedPointPosition;
70         number = number.replace(",", ".");
71         auto twoParts = number.split(".");
72         if (twoParts.length > 2 || !std..string.isNumeric(number)) {
73             throw new DecimalException("Wrong data format");
74         }
75 
76         auto integer = twoParts[0].to!(char[]);
77         int sign = 1;
78         if (integer[0] == '-') {
79             sign = -1;
80         }
81         if (fixedPoint != 0) {
82             integer.length += fixedPoint;
83             integer[$-fixedPoint .. $] = '0';
84         }
85         value = BigInt(integer);
86 
87         if (twoParts.length == 2 && fixedPoint != 0) {
88             auto fraction = twoParts[1].to!(char[]);
89             if (fraction.length > fixedPoint) {
90                 fraction.length = fixedPoint;
91             } else {
92                 size_t oldLength = fraction.length;
93                 fraction.length = fixedPoint;
94                 fraction[oldLength .. $] = '0';
95             }
96             value += sign * BigInt(fraction);
97         }
98     }
99 
100     /***************************************************************************
101      * Constucts an object from another Decimal object.
102      * This constructor is used to transforming precision.
103      *
104      * Params:
105      *     number = Decimal object.
106      *     fixedPointPosition = Position of decimal point in new object.
107      */
108     this(Decimal number, size_t fixedPointPosition = defaultFixedPoint) {
109         fixedPoint = fixedPointPosition;
110         if (fixedPoint == number.fixedPoint) {
111             value = number.value;
112         } else if (fixedPoint > number.fixedPoint) {
113             auto diff = fixedPoint - number.fixedPoint;
114             value = number.value * (BigInt(10)^^diff);
115         } else if (number.fixedPoint > fixedPoint) {
116             auto diff = number.fixedPoint - fixedPoint;
117             BigInt rem;
118             BigInt divisor = BigInt(10)^^diff;
119             divMod(number.value, divisor, value, rem);
120             // if the remainder is one order of magnitude less than the divisor
121             if (divisor < absBigInt(rem*10)) {
122                 value += calcRoundingCompensation(rem);
123             }
124         }
125     }
126 
127     /***************************************************************************
128      * Constucts an object from a BigInt object.
129      *
130      * Params:
131      *     number = BigInt
132      *     fixedPointPosition = Position of decimal point in new object.
133      */
134     this(BigInt number, size_t fixedPointPosition = defaultFixedPoint) {
135         fixedPoint = fixedPointPosition;
136         value = number * BigInt(10)^^fixedPoint;
137     }
138 
139     /***************************************************************************
140      * Constucts an object from an integer number (int/uint, long/ulong, etc.).
141      *
142      * Params:
143      *     number = Integer number of a built-in data type.
144      *     fixedPointPosition = Position of decimal point in new object.
145      */
146     this(T)(T number, size_t fixedPointPosition = defaultFixedPoint)
147     if (is(T: long)) {
148         fixedPoint = fixedPointPosition;
149         value = BigInt(number) * BigInt(10)^^fixedPoint;
150     }
151 
152     /***************************************************************************
153      * Constucts an object from a floating point number.
154      * (For good precision,
155      * give preference to forming a decimal number from a string.)
156      *
157      * Params:
158      *     number = Floating point number of a built-in data type.
159      *     fixedPointPosition = Position of decimal point in new object.
160      */
161     this(real number, size_t fixedPointPosition = defaultFixedPoint) {
162         if (std.math.isNaN(number) || std.math.isInfinity(number)) {
163             throw new DecimalException("Unsupported floating value");
164         }
165         string fmt = "%f";
166         string strValue = format(fmt, number);
167         this(strValue, fixedPointPosition);
168     }
169 
170     /***************************************************************************
171      * Converts to a human readable decimal string.
172      * All characters after the dot (with all zeros) will be shown according
173      * to the configured fixed point position.
174      */
175     string toFullString() const {
176         auto strValue = format!"%d"(value).to!(char[]);
177         int sign = 1;
178         if (strValue[0] == '-') {
179             sign = -1;
180             strValue = strValue[1 .. $];
181         }
182         if (strValue.length <= fixedPoint) {
183             char[] zeros;
184             zeros.length = fixedPoint - strValue.length;
185             zeros[] = '0';
186             strValue = "0" ~ zeros ~ strValue;
187         }
188         auto res = strValue[0 .. $-fixedPoint];
189         if (fixedPoint != 0) {
190              res ~= "." ~ strValue[$-fixedPoint .. $];
191         }
192         if (sign == -1) {
193             res = "-" ~ res;
194         }
195         return res.to!string;
196     }
197 
198     /***************************************************************************
199      * Convert the `Decimal` to `string`, passing it to the given sink.
200      *
201      * Params:
202      *     sink = An OutputRange for accepting possibly piecewise
203      *            segments of the  formatted string.
204      *     fmt = A format string specifying the output format.
205      *
206      * Available: %D (or %f instead it) and %s.
207      * For %D (or %f) there are available '+', '0' and width with precision
208      * like for floating. For example, "%+08.5D".
209      */
210     void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt)
211     const {
212         immutable defaultFormatPrecision = fmt.UNSPECIFIED;
213         if (fmt.spec == 'D' || fmt.spec == 'f') {
214             auto prec = fmt.precision;
215             if (prec == defaultFormatPrecision && this.fixedPoint < int.max) {
216                 prec = cast(int)this.fixedPoint;
217             }
218             string[] parts = this.toFullString().split('.');
219             if (parts.length == 1) {
220                 parts ~= makeFilledArray(prec, '0').idup;
221             } else {
222                 if (prec > this.fixedPoint) {
223                     parts[1] ~= makeFilledArray(prec-this.fixedPoint, '0');
224                 } else {
225                     auto rounded = this.round(prec);
226                     string[] roundedParts = rounded.toFullString().split('.');
227                     parts[0] = roundedParts[0];
228                     if (parts.length == 2) {
229                         parts[1] = roundedParts[1][0 .. prec];
230                     } else {
231                         parts[1] = "";
232                     }
233                 }
234             }
235             string result = parts[1] != "" ? parts[0] ~ "." ~ parts[1]
236                                            : parts[0];
237             if (result.length < fmt.width) {
238                 char filler = fmt.flZero ? '0' : ' ';
239                 char[] s = makeFilledArray(fmt.width-result.length, filler);
240                 if (fmt.flPlus) s[0] = '+';
241                 result = s.idup ~ result;
242             } else if (fmt.flPlus) {
243                 result = "+" ~ result;
244             }
245             sink(result);
246         } else if (fmt.spec == 's') {
247             sink(this.toFullString.stripRight('0').stripRight('.'));
248         } else {
249             throw new Exception("Unknown format specifier: %" ~ fmt.spec);
250         }
251     }
252 
253     /***************************************************************************
254      * Converts to a boolean value.
255      */
256     T opCast(T:bool)() {
257         return !isZero();
258     }
259 
260     /***************************************************************************
261      * Converts to a floating type, if possible.
262      */
263     T opCast(T:real)() {
264         return this.toFullString.stripRight('0').to!T;
265     }
266 
267     /***************************************************************************
268      * Converts to an integer type, if possible.
269      */
270     T opCast(T:long)() {
271         string integerPart = this.toFullString().split(".")[0];
272         return integerPart.to!T;
273     }
274 
275     /***************************************************************************
276      * Gets the modulus.
277      */
278     Decimal abs() pure const {
279         auto copy = this;
280         if (this.value < 0) {
281             return -copy;
282         }
283         return copy;
284     }
285 
286     /***************************************************************************
287      * Calculates and returns the rounded value.
288      * The fixed point position in the return value remains the original.
289      * Params:
290      *     lastDigitPosition = The number of decimal places to save.
291      *                         By default, 0.
292      */
293     Decimal round(size_t lastDigitPosition = 0) pure const {
294         Decimal copy = this;
295         if (fixedPoint == 0 || lastDigitPosition >= fixedPoint) {
296             return copy;
297         }
298         auto diff = fixedPoint - lastDigitPosition;
299         BigInt rem;
300         BigInt divisor = BigInt(10)^^diff;
301         divMod(copy.value, divisor, copy.value, rem);
302         if (divisor < absBigInt(rem*10)) {
303             copy.value += calcRoundingCompensation(rem);
304         }
305         copy.value *= BigInt(10)^^diff;
306         return copy;
307     }
308 
309     /***************************************************************************
310      * Gets a sign of the decimal number (-1, 0 or 1).
311      */
312     int getSign() pure const {
313         if (this.value < 0) {
314             return -1;
315         } else if (this.value == 0) {
316             return 0;
317         }
318         return 1;
319     }
320 
321     /***************************************************************************
322      * Implements Decimal equality test with other Decimal.
323      */
324     bool opEquals(Decimal rhs) {
325         string left = this.toFullString.stripRight('0').stripRight('.');
326         string right = rhs.toFullString.stripRight('0').stripRight('.');
327         return left == right;
328     }
329 
330     /***************************************************************************
331      * Implements Decimal equality test with buil-in numeric types and strings.
332      */
333     bool opEquals(T)(T rhs) {
334         auto right = Decimal(rhs);
335         return this.opEquals(right);
336     }
337 
338     /***************************************************************************
339      * Implements comparison of Decimal with other Decimal.
340      */
341     int opCmp(Decimal rhs) {
342         if (this.opEquals(rhs)) {
343             return 0;
344         }
345         if (this.fixedPoint == rhs.fixedPoint && this.value > rhs.value) {
346             return 1;
347         } else if (this.fixedPoint != rhs.fixedPoint) {
348             auto lhs = this;
349             leadToUnifiedFixedPoint(lhs, rhs);
350             if (lhs.value > rhs.value) {
351                 return 1;
352             }
353         }
354         return -1;
355     }
356 
357     /***************************************************************************
358      * Implements comparison of Decimal with other numeric types and strings.
359      */
360     int opCmp(T)(T rhs) {
361         return this.opCmp(Decimal(rhs));
362     }
363 
364     /***************************************************************************
365      * Implements Decimal unary operator "++".
366      */
367     ref Decimal opUnary(string op)() if (op == "++") {
368         auto appendum = BigInt(1) * BigInt(10)^^fixedPoint;
369         value += appendum;
370         return this;
371     }
372 
373     /***************************************************************************
374      * Implements Decimal unary operator "--".
375      */
376     ref Decimal opUnary(string op)() if (op == "--") {
377         auto subtrahend = BigInt(1) * BigInt(10)^^fixedPoint;
378         value -= subtrahend;
379         return this;
380     }
381 
382     /***************************************************************************
383      * Implements Decimal unary operator "+".
384      */
385     Decimal opUnary(string op)() if (op == "+") {
386         return this;
387     }
388 
389     /***************************************************************************
390      * Implements Decimal unary operator "-".
391      */
392     Decimal opUnary(string op)() pure const if (op == "-") {
393         Decimal res = this;
394         res.value = -this.value;
395         return res;
396     }
397 
398     /***************************************************************************
399      * Implements binary operators between Decimals.
400      * Returns an object with the maximum precision of both original objects.
401      */
402     Decimal opBinary(string op)(Decimal rhs) const
403     if (op.among("+", "-", "*", "/", "^^")) {
404         static if (op == "^^") {
405             return pow(this, rhs);
406         }
407 
408         Decimal lhs = this; //copied current state
409         size_t bestPrecision = leadToUnifiedFixedPoint(lhs, rhs);
410         auto res = Decimal(0, bestPrecision);
411 
412         static if (op == "+") {
413             res.value = lhs.value + rhs.value;
414         }
415         static if (op == "-") {
416             res.value = lhs.value - rhs.value;
417         }
418         static if (op == "*") {
419             res.value = lhs.value * rhs.value;
420             BigInt rem;
421             BigInt divisor = BigInt(10)^^bestPrecision;
422             divMod(res.value, divisor, res.value, rem);
423             if (divisor < absBigInt(rem*10)) {
424                 res.value += calcRoundingCompensation(rem);
425             }
426         }
427         static if (op == "/") {
428             if (rhs.value == BigInt(0)) {
429                 throw new DecimalException("Division by zero");
430             }
431             BigInt rem;
432             lhs.value *= BigInt(10)^^bestPrecision;
433             divMod(lhs.value, rhs.value, res.value, rem);
434             if (rhs.value < absBigInt(rem*10)) {
435                 res.value += calcRoundingCompensation(rem);
436             }
437         }
438         return res;
439     }
440 
441     /***************************************************************************
442      * Implements binary operators between Decimal and build-in type values.
443      *
444      * The precision of the decimal result is equal
445      * to the precision of the decimal argument.
446      */
447     Decimal opBinary(string op, T)(T rhs) const
448     if (op.among("+", "-", "*", "/", "^^")) {
449         static if (op == "^^") {
450             return pow(this, Decimal(rhs, this.fixedPoint));
451         }
452         return this.opBinary!op(Decimal(rhs, this.fixedPoint));
453     }
454 
455     /***************************************************************************
456      * Implements operators with built-in integers on the left-hand side
457      * and `Decimal` on the right-hand side.
458      *
459      * The precision of the decimal result is equal
460      * to the precision of the decimal argument.
461      */
462     Decimal opBinaryRight(string op, T)(T value) const
463     if (op.among("+", "*", "-", "/", "^^")) {
464         auto decValue = Decimal(value, this.fixedPoint);
465         static if (op == "^^") {
466             return pow(decValue, this);
467         }
468         return decValue.opBinary!op(this);
469     }
470 
471     /***************************************************************************
472      * Implements assignment operators of the form `Decimal op= integer`:
473      * "+=", "-=", "*=", "/=", "^^=".
474      * Position of the fixed point (precision) doesn't change.
475      */
476     Decimal opOpAssign(string op, T)(T value)
477     if (op.among("+", "-", "*", "/", "^^")) {
478         Decimal rhs = Decimal(value, this.fixedPoint);
479         static if (op == "+") this.value += rhs.value;
480         static if (op == "-") this.value -= rhs.value;
481         static if (op == "*") {
482             this.value *= rhs.value;
483             BigInt rem;
484             BigInt divisor = BigInt(10)^^this.fixedPoint;
485             divMod(this.value, divisor, this.value, rem);
486             if (divisor < absBigInt(rem*10)) {
487                 this.value += calcRoundingCompensation(rem);
488             }
489         }
490         static if (op == "/") {
491             if (rhs.value == BigInt(0)) {
492                 throw new DecimalException("Division by zero");
493             }
494             this.value *= BigInt(10)^^this.fixedPoint;
495             BigInt rem;
496             divMod(this.value, rhs.value, this.value, rem);
497             if (rhs.value < absBigInt(rem*10)) {
498                 this.value += calcRoundingCompensation(rem);
499             }
500         }
501         static if (op == "^^") {
502             Decimal res = pow(this, rhs);
503             this = Decimal(res, this.fixedPoint);
504         }
505         return this;
506     }
507 
508     /***************************************************************************
509      * Check if Decimal has zero fractional part.
510      */
511     bool isInteger() {
512         if (fixedPoint == 0) {
513             return true;
514         }
515         string fractionalPart = toFullString().split(".")[1];
516         return all!"a=='0'"(fractionalPart);
517     }
518 
519     /***************************************************************************
520      * Check if Decimal has zero value.
521      */
522     bool isZero() {
523         return this.value == 0;
524     }
525 
526 private:
527 
528     size_t leadToUnifiedFixedPoint(ref Decimal lhs, ref Decimal rhs)
529     pure const {
530         size_t bestPrecision, worstPrecision;
531         if (lhs.fixedPoint > rhs.fixedPoint) {
532             bestPrecision = lhs.fixedPoint;
533             worstPrecision = rhs.fixedPoint;
534             rhs.value *= BigInt(10)^^(bestPrecision-worstPrecision);
535         } else {
536             worstPrecision = this.fixedPoint;
537             bestPrecision = rhs.fixedPoint;
538             lhs.value *= BigInt(10)^^(bestPrecision-worstPrecision);
539         }
540         return bestPrecision;
541     }
542 
543     BigInt calcSignForBigInt(BigInt value) pure const {
544         if (value > 0) return BigInt(1);
545         if (value == 0) return BigInt(0);
546         return BigInt(-1);
547     }
548 
549     BigInt absBigInt(BigInt value) pure const {
550         return value >= 0 ? value : -value;
551     }
552 
553     BigInt calcRoundingCompensation(BigInt remainder) pure const {
554         BigInt absRemainder = absBigInt(remainder);
555         BigInt sign = calcSignForBigInt(remainder);
556         if (remainder % 10 == remainder) {
557             if (remainder >= 0) {
558                 if (remainder % 10 >= 5) {
559                     return BigInt(1);
560                 }
561             } else {
562                 if (-remainder % 10 >= 5) {
563                     return BigInt(-1);
564                 }
565             }
566             return BigInt(0);
567         }
568         size_t calcDigits(BigInt value) {
569             return absBigInt(value).toDecimalString.length;
570         }
571         if (absRemainder % 10 >= 5) {
572             if (calcDigits(absRemainder) != calcDigits(absRemainder + 1)) {
573                 return sign; // overflow
574             }
575             remainder = (remainder + sign) / 10;
576         } else {
577             remainder = remainder / 10;
578         }
579         return calcRoundingCompensation(remainder);
580     }
581 }
582 
583 
584 /*******************************************************************************
585  * Calculates e^x for Decimal.
586  */
587 Decimal exp(Decimal x) {
588     auto precision = x.fixedPoint;
589     auto additPrecision = precision * 2 + 3;
590     Decimal res = x + Decimal(1, additPrecision);
591     Decimal numerator = Decimal(x, additPrecision);
592     Decimal denominator = Decimal(1, additPrecision);
593     foreach (i; 2 .. additPrecision) {
594         numerator = numerator * x;
595         denominator = denominator * i;
596         res = res + numerator/denominator;
597     }
598     return Decimal(res, precision);
599 }
600 
601 
602 /*******************************************************************************
603  * Calculates the natural logarithm x (for `real`).
604  * The precision is 18 decimal places.
605  */
606 real ln(real x) {
607     auto numerator = (x-1)/(x+1);
608     auto n2 = numerator * numerator;
609     auto res = numerator;
610     real elem; // row element
611     real denominator = 3;
612     do {
613         numerator = numerator * n2;
614         elem = numerator / denominator;
615         res = res + elem;
616         denominator = denominator + 2;
617     } while (elem > 0.000000000000000001);
618 
619     return res * 2;
620 }
621 
622 
623 /*******************************************************************************
624  * Calculates the natural logarithm x of the Decimal type.
625  */
626 Decimal ln(Decimal x) {
627     auto precision = x.fixedPoint;
628     auto additPrecision = x.fixedPoint * 2 + 3;
629     auto extendedX = Decimal(x, additPrecision);
630     auto numerator = (extendedX-1)/(extendedX+1);
631     auto n2 = numerator * numerator;
632     auto res = numerator;
633     Decimal elem = Decimal(0, additPrecision); // row element
634     Decimal denominator = Decimal(3, additPrecision);
635     char[] zeros = makeFilledArray(precision, '0');
636     string strMin = "0." ~ zeros.idup ~ "1";
637     auto minValue = Decimal(strMin, additPrecision);
638     do {
639         numerator = numerator * n2;
640         elem = numerator / denominator;
641         res = res + elem;
642         denominator = denominator + 2;
643     } while (elem > minValue);
644 
645     res.value *= 2;
646     return Decimal(res, x.fixedPoint);
647 }
648 
649 
650 /*******************************************************************************
651  * Calculates the logarithm.
652  *
653  * Params:
654  *     b = Decimal value
655  *     a = base of the logarithm
656  */
657 Decimal log(Decimal b, Decimal a) {
658     auto precision = b.fixedPoint > a.fixedPoint ? b.fixedPoint : a.fixedPoint;
659     auto additPrecison = precision * 2 + 3;
660     b = Decimal(b, additPrecison);
661     a = Decimal(a, additPrecison);
662     return Decimal(ln(b) / ln(a), precision);
663 }
664 
665 
666 /*******************************************************************************
667  * Returns the value of x raised to the power of y.
668  *
669  * Params:
670  *     x = Decimal raised to a power
671  *     y = exponent of the ulong type
672  */
673 Decimal pow(Decimal x, ulong y) {
674     if (y == 0) {
675         return Decimal(1, x.fixedPoint);
676     } else if (y == 1) {
677         return x;
678     }
679     Decimal res = x;
680     foreach(i; 1 .. y) {
681         res = res * x;
682     }
683     return res;
684 }
685 
686 
687 /*******************************************************************************
688  * Returns the value of x raised to the power of y.
689  *
690  * Params:
691  *    x = Decimal raised to a power
692  *    y = Decimal exponent
693  */
694 Decimal pow(Decimal x, Decimal y) {
695     size_t digits = x.fixedPoint > y.fixedPoint ? x.fixedPoint : y.fixedPoint;
696     x = Decimal(x, digits*2+8);
697     y = Decimal(y, digits*2+8);
698     auto res = exp(y * ln(x));
699     return Decimal(res, digits);
700 }
701 
702 
703 /*******************************************************************************
704  * Base exeption for this module
705  */
706 class DecimalException : Exception { mixin RealizeException; }
707 
708 
709 
710 /*******************************************************************************
711 ********************************** UNITTESTS ***********************************
712 *******************************************************************************/
713 
714 // constructors
715 unittest {
716     auto x = Decimal("36");
717     assert(x.value == BigInt("36000000000000000000"));
718     assert(x.toFullString == "36.000000000000000000");
719 
720     x = Decimal("36.6");
721     assert(x.value == BigInt("36600000000000000000"));
722     assert(x.toFullString == "36.600000000000000000");
723 
724     x = Decimal("36,6");
725     assert(x.value == BigInt("36600000000000000000"));
726     assert(x.toFullString == "36.600000000000000000");
727 
728     x = Decimal("-36,6");
729     assert(x.value == BigInt("-36600000000000000000"));
730     assert(x.toFullString == "-36.600000000000000000");
731 
732     x = Decimal(36);
733     assert(x.value == BigInt("36000000000000000000"));
734     assert(x.toFullString == "36.000000000000000000");
735 
736     x = Decimal(-36);
737     assert(x.value == BigInt("-36000000000000000000"));
738     assert(x.toFullString == "-36.000000000000000000");
739 
740     x = Decimal(-36.6, 4);
741     assert(x.value == BigInt("-366000"));
742     assert(x.toFullString == "-36.6000");
743 
744     x = Decimal("0");
745     assert(x.value == BigInt("0"));
746     assert(x.toFullString == "0.000000000000000000");
747 
748     x = Decimal(0);
749     assert(x.value == BigInt("0"));
750     assert(x.toFullString == "0.000000000000000000");
751 
752     x = Decimal(0.0);
753     assert(x.value == BigInt("0"));
754     assert(x.toFullString == "0.000000000000000000");
755 
756     x = Decimal(-0.0);
757     assert(x.value == BigInt("0"));
758     assert(x.toFullString == "0.000000000000000000");
759 
760     x = Decimal("0.12");
761     assert(x.value == BigInt("120000000000000000"));
762     assert(x.toFullString == "0.120000000000000000");
763 
764     x = Decimal(0.12);
765     assert(x.value == BigInt("120000000000000000"));
766     assert(x.toFullString == "0.120000000000000000");
767 
768     x = Decimal("-0.12");
769     assert(x.value == BigInt("-120000000000000000"));
770     assert(x.toFullString == "-0.120000000000000000");
771 
772     x = Decimal(-0.12);
773     assert(x.value == BigInt("-120000000000000000"));
774     assert(x.toFullString == "-0.120000000000000000");
775 
776     x = Decimal("-0.02");
777     assert(x.value == BigInt("-20000000000000000"));
778     assert(x.toFullString == "-0.020000000000000000");
779 
780     x = Decimal(-0.02);
781     assert(x.value == BigInt("-20000000000000000"));
782     assert(x.toFullString == "-0.020000000000000000");
783 
784     x = Decimal("0.1");
785     assert(x.value == BigInt("100000000000000000"));
786     assert(x.toFullString == "0.100000000000000000");
787 
788     x = Decimal(0.1);
789     assert(x.value == BigInt("100000000000000000"));
790     assert(x.toFullString == "0.100000000000000000");
791 
792     x = Decimal("-0.00123");
793     assert(x.value == BigInt("-1230000000000000"));
794     assert(x.toFullString == "-0.001230000000000000");
795 
796     x = Decimal(-0.00123);
797     assert(x.value == BigInt("-1230000000000000"));
798     assert(x.toFullString == "-0.001230000000000000");
799 
800     x = Decimal(BigInt(36));
801     assert(x.value == BigInt("36000000000000000000"));
802     assert(x.toFullString == "36.000000000000000000");
803 }
804 
805 // error data
806 unittest {
807     bool error = false;
808     Decimal x;
809     try x = Decimal("34.2.55");
810     catch (DecimalException e) error = true;
811     assert(error);
812 
813     error = false;
814     try x = Decimal("");
815     catch (DecimalException e) error = true;
816     assert(error);
817 
818     error = false;
819     try x = Decimal("23.1A");
820     catch (DecimalException e) error = true;
821     assert(error);
822 
823     error = false;
824     try x = Decimal(float.infinity);
825     catch(DecimalException e) error = true;
826     assert(error);
827 
828     error = false;
829     try x = Decimal(float.nan);
830     catch(DecimalException e) error = true;
831     assert(error);
832 
833     error = false;
834     try x = Decimal(2)/Decimal(0);
835     catch(DecimalException e) error = true;
836     assert(error);
837 
838     error = false;
839     x = Decimal("9999999999999999999999999999999999");
840     try writefln("%d", x.to!long);
841     catch(std.conv.ConvOverflowException e) error = true;
842     assert(error);
843 
844     error = false;
845     x = Decimal(65536);
846     try writefln("%d", x.to!ushort);
847     catch(std.conv.ConvOverflowException e) error = true;
848     assert(error);
849 }
850 
851 //transform/copy constructor
852 unittest {
853     auto x = Decimal("6.54321", 6);
854     assert(x.toFullString == "6.543210");
855     auto y = Decimal(x, 4);
856     assert(x != y);
857     assert(y.toFullString == "6.5432");
858     auto z = Decimal(x, 7);
859     assert(x == z);
860     assert(z.toFullString == "6.5432100");
861 }
862 
863 
864 //convertation
865 unittest {
866     auto x = Decimal("5", 4);
867     assert(x.toFullString == "5.0000");
868     assert(x.to!string == "5");
869     assert(x.to!real == 5.0);
870     assert(x.to!long == 5);
871     x = Decimal("5.8");
872     assert(x.to!long == 5);
873     x = Decimal("-5.8");
874     assert(x.to!long == -5);
875     assert(x.to!bool == true);
876     x = Decimal("0.0000000000000000000000000000001", 50);
877     assert(x.to!bool == true);
878     x = Decimal(0);
879     assert(x.to!bool == false);
880 }
881 
882 // usage of output format
883 unittest {
884     Decimal x = Decimal("5.123456789");
885     assert(format!"%2.7D"(x) == "5.1234568");
886     assert(format!"%.7D"(x) == "5.1234568");
887     assert(format!"%.12D"(x) == "5.123456789000");
888     assert(format!"%D"(x) == "5.123456789000000000");
889     assert(format!"%8.2D"(x) == "    5.12");
890     assert(format!"%08.2D"(x) == "00005.12");
891     assert(format!"%+08.2D"(x) == "+0005.12");
892     assert(format!"%+.2D"(x) == "+5.12");
893     assert(format!"%+.0D"(x) == "+5");
894     Decimal zero = Decimal(0);
895     assert(format!"%.8D"(zero) == "0.00000000");
896     assert(format!"%D"(zero) == "0.000000000000000000");
897     assert(format!"%s"(zero) == "0");
898 }
899 
900 
901 // comparison
902 unittest {
903     auto x = Decimal(-36.6, 4);
904     assert(Decimal("-36.6") == x);
905 
906     assert(Decimal("0") == Decimal(-0.0));
907 
908     assert(Decimal(-0.0002) < Decimal(-0.0001));
909     assert(Decimal(-0.0001) >= Decimal(-0.0001));
910     assert(Decimal(-0.0001, 8) == Decimal(-0.0001, 5));
911     assert(Decimal(-0.0003, 17) == Decimal(-0.0003, 18));
912     assert(Decimal(0.0004, 5) > Decimal(0.00035, 8));
913     assert(Decimal(0.0004, 8) > Decimal(0.00035, 5));
914     assert(Decimal(-0.0004, 8) < Decimal(0.00035, 5));
915     assert(Decimal(-0.0004, 5) < Decimal(0.00035, 8));
916 
917     assert(Decimal(1) == 1);
918     assert(1 == Decimal(1));
919 
920     assert(Decimal(100.5) == 100.5);
921     assert(Decimal(100.6) > 100.5);
922 }
923 
924 
925 // precision
926 unittest {
927     auto x = Decimal("36.6", 4);
928     assert(x.toFullString == "36.6000");
929 
930     x = Decimal("36,654321", 3);
931     assert(x.toFullString == "36.654");
932 
933     x = Decimal("-36,654321", 3);
934     assert(x.toFullString == "-36.654");
935 
936     x = Decimal(-36.654321, 3);
937     assert(x.toFullString == "-36.654");
938 
939     x = Decimal("36,654321", 0);
940     assert(x.toFullString == "36");
941 
942     x = Decimal("-36,654321", 0);
943     assert(x.toFullString == "-36");
944 
945     x = Decimal(-36.654321, 0);
946     assert(x.toFullString == "-36");
947 
948     x = Decimal("-36.654321", 0);
949     assert(x.toFullString == "-36");
950 
951     x = Decimal("-0.03", 0);
952     assert(x.toFullString == "0");
953 
954     x = Decimal(100, 2);
955     assert(x.toFullString == "100.00");
956 
957     x = Decimal(100, 0);
958     assert(x.toFullString == "100");
959 
960     // round
961     x = Decimal(99.79, 2);
962     assert(Decimal(x, 1).toFullString == "99.8");
963 }
964 
965 //unary operators
966 unittest {
967     auto x = Decimal(0);
968     x++;
969     assert(x == Decimal(1));
970 
971     x = Decimal(-0.4);
972     x++;
973     assert(x == Decimal(0.6));
974     x--;
975     assert(x == Decimal(-0.4));
976 
977     x = Decimal(0);
978     x--;
979     assert(x == Decimal(-1));
980 
981     x = Decimal(-9);
982     assert(x == -Decimal(9));
983 
984     x = -Decimal(0.3);
985     assert(x == Decimal(-0.3));
986 }
987 
988 // plus/minus
989 unittest {
990     assert(2.3.D + 7.8.D == 10.1.D);
991     assert(Decimal("0.5") + Decimal("-0.7") == Decimal("-0.2"));
992     assert(Decimal("20.9") - Decimal("5.87") == Decimal("15.03"));
993     assert(Decimal("20.39", 2) + Decimal(11.125, 3) == Decimal("31.515"));
994 
995     assert(Decimal("0.6") + 0.6 == Decimal("1.2"));
996     assert(0.6 + Decimal("0.6") == Decimal("1.2"));
997     assert(Decimal("1.5") - 1.7 == Decimal("-0.2"));
998     assert(1.5 - Decimal(1.7) == Decimal("-0.2"));
999 }
1000 
1001 // +=
1002 unittest {
1003     auto x = Decimal(5);
1004     auto y = Decimal(7);
1005     x += y;
1006     assert(x == Decimal(12));
1007     assert(y == Decimal(7)); // no effects
1008 
1009     x = Decimal("-0.1");
1010     y = Decimal("2.05");
1011     y += x;
1012     assert(y == Decimal("1.95"));
1013     assert(x == Decimal("-0.1"));
1014 
1015     x = Decimal("4.12345", 5);
1016     y = Decimal("0.0000001");
1017     x += y;
1018     assert(x == Decimal("4.12345")); // no effects
1019     assert(x.fixedPoint == 5); // no effects
1020 
1021     x = Decimal("4.12345", 5);
1022     y = Decimal("0.000009");
1023     x += y;
1024     assert(x == Decimal("4.12346"));
1025     assert(x.fixedPoint == 5); // no effects
1026 
1027     // other data types
1028     x = Decimal(7);
1029     x += 2;
1030     assert(x == Decimal(9));
1031     x -= 1.7;
1032     assert(x == Decimal(7.3));
1033     x += "11.1";
1034     assert(x == Decimal(18.4));
1035 }
1036 
1037 // -=
1038 unittest {
1039     auto x = Decimal(5);
1040     auto y = Decimal(7);
1041     x -= y;
1042     assert(x == Decimal(-2));
1043     assert(y == Decimal(7)); // no effects
1044 
1045     x = Decimal("-0.1");
1046     y = Decimal("2.05");
1047     y -= x;
1048     assert(y == Decimal("2.15"));
1049     assert(x == Decimal("-0.1")); // no effects
1050 
1051     x = Decimal("4.12345", 5);
1052     y = Decimal("0.0000001");
1053     x -= y;
1054     assert(x == Decimal("4.12345")); // no effects
1055     assert(x.fixedPoint == 5); // no effects
1056     assert(y.fixedPoint == defaultFixedPoint); // no effects
1057 
1058     x = Decimal("4.12345", 5);
1059     y = Decimal("0.000009");
1060     x -= y;
1061     assert(x == Decimal("4.12344"));
1062     assert(x.fixedPoint == 5); // no effects
1063 }
1064 
1065 //mutliplication
1066 unittest {
1067     auto x = 3.D;
1068     auto y = 2.D;
1069     assert(x*y == 6.D);
1070     x = 2809.9324.D;
1071     y = 2.D;
1072     assert(x*y == "5619.8648".D);
1073     y = 2.7.D;
1074     assert(x*y == "7586,81748".D);
1075     y = 2.75.D;
1076     assert(x*y == "7727.3141".D);
1077     y = 2.05.D;
1078     assert(x*y == "5760.36142".D);
1079     x = "3.08".D;
1080     y = -2.D;
1081     assert(x*y == "-6.16".D);
1082     auto pi = "3.141592653589793238".D;
1083     assert(pi*pi == "9.869604401089358616".D);
1084     auto e = "2.718281828459045".D;
1085     assert(e*e == "7.389056098930648948".D);
1086     x = "0.3".D;
1087     y = 10.D;
1088     assert(x*y == 3.D);
1089     assert(100.D == 10.D * 10.D);
1090     assert(0.01.D == 0.1.D * 0.1.D);
1091     assert(0.001.D == "0.01".D * "0.1".D);
1092     assert(0.001.D == "0.1".D * "0.01".D);
1093 
1094     assert(2.5.D * 2 == 5.D);
1095     assert(2 * 2.5.D * 2 == 10.D);
1096 
1097     // check rounding
1098     x = Decimal("0.1", 2);
1099     y = Decimal("2.05", 2);
1100     auto z = x * y;
1101     assert(z == Decimal("0.21", 2));
1102     x = Decimal("-0.1", 2);
1103     y = Decimal("2.05", 2);
1104     z = x * y;
1105     assert(z == Decimal("-0.21", 2));
1106 }
1107 
1108 //division
1109 unittest {
1110     assert(5.D / 2.D == 2.5.D);
1111     assert(5 / 2.D == 2.5.D);
1112     assert(5.D / 2 == 2.5.D);
1113     assert(0.D / 2.D == 0.D);
1114     assert("-13.50".D / "2.25".D == "-6".D);
1115     assert(Decimal("-13.50", 10) / Decimal("2.25", 4) == "-6".D);
1116     assert(1.D == 1.D/BigInt(1));
1117     assert(1.D == Decimal(1, 10)/BigInt(1));
1118     assert(1.D == Decimal(1, 25)/BigInt(1));
1119     assert(0.5.D == 1.D/BigInt(2));
1120 
1121     auto x = Decimal(5);
1122     auto y = Decimal(7);
1123     assert(x/y == Decimal("0.714285714285714286"));
1124 }
1125 
1126 // *=
1127 unittest {
1128     auto x = Decimal(5);
1129     auto y = Decimal(7);
1130     x *= y;
1131     assert(x == Decimal(35));
1132     assert(y == Decimal(7)); // no effects
1133 
1134     x = Decimal("-0.1");
1135     y = Decimal("2.05");
1136     y *= x;
1137     assert(y == Decimal("-0.205"));
1138     assert(x == Decimal("-0.1")); // no effects
1139 
1140     x = Decimal("4.12345", 5);
1141     y = Decimal("0.0000001");
1142     x *= y;
1143     assert(x == Decimal(0));
1144     assert(x.fixedPoint == 5); // no effects
1145     assert(y.fixedPoint == defaultFixedPoint); // no effects
1146 
1147     x = Decimal("4.12345", 5);
1148     y = Decimal("0.000009");
1149     x *= y;
1150     assert(x == Decimal(0.00004));
1151     assert(x.fixedPoint == 5); // no effects
1152 
1153     x = Decimal("-0.1");
1154     y = Decimal("2.05");
1155     y *= x;
1156     assert(y == Decimal("-0.205"));
1157     assert(x == Decimal("-0.1")); // no effects
1158 
1159     // check rounding
1160     x = Decimal("0.1", 2);
1161     y = Decimal("2.05", 2);
1162     y *= x;
1163     assert(y == Decimal("0.21", 2));
1164     assert(x == Decimal("0.1", 2)); // no effects
1165     x = Decimal("-0.1", 2);
1166     y = Decimal("2.05", 2);
1167     y *= x;
1168     assert(y == Decimal("-0.21", 2));
1169     assert(x == Decimal("-0.1", 2)); // no effects
1170 
1171     x = Decimal("4.12345", 5);
1172     y = Decimal("0.0000001");
1173     x *= y;
1174     assert(x == Decimal(0));
1175     assert(x.fixedPoint == 5); // no effects
1176     assert(y.fixedPoint == defaultFixedPoint); // no effects
1177 
1178     x = Decimal("4.12345", 5);
1179     y = Decimal("0.000009");
1180     x *= y;
1181     assert(x == Decimal(0.00004));
1182     assert(x.fixedPoint == 5); // no effects
1183 }
1184 
1185 // /=
1186 unittest {
1187     auto x = Decimal(5);
1188     auto y = Decimal(7);
1189     x /= y;
1190     assert(x == Decimal("0.714285714285714286"));
1191     assert(y == Decimal(7)); // no effects
1192 
1193     x = Decimal(-5);
1194     y = Decimal(7);
1195     x /= y;
1196     assert(x == Decimal("-0.714285714285714286"));
1197     assert(y == Decimal(7)); // no effects
1198 
1199 }
1200 
1201 //abs, round
1202 unittest {
1203     auto x = Decimal("-0.725");
1204     assert(x.abs == Decimal("0.725"));
1205     x = Decimal("4.9");
1206     assert(x.abs == Decimal("4.9"));
1207 
1208     x = Decimal("43.5");
1209     assert(x.round() == Decimal("44"));
1210     x = Decimal("-35.1");
1211     assert(x.round() == Decimal("-35"));
1212     x = Decimal("-35.9");
1213     assert(x.round() == Decimal("-36"));
1214 
1215     x = Decimal("77.383838");
1216     auto rounded = x.round();
1217     assert(rounded == Decimal("77"));
1218     assert(rounded.fixedPoint == Decimal(x.fixedPoint));
1219     rounded = x.round(1);
1220     assert(rounded == Decimal("77.4"));
1221     assert(rounded.fixedPoint == Decimal(x.fixedPoint));
1222     rounded = x.round(2);
1223     assert(rounded == Decimal("77.38"));
1224     assert(rounded.fixedPoint == Decimal(x.fixedPoint));
1225     rounded = x.round(3);
1226     assert(rounded == Decimal("77.384"));
1227     assert(rounded.fixedPoint == Decimal(x.fixedPoint));
1228     x = Decimal("-77.383838");
1229     rounded = x.round(3);
1230     assert(rounded == Decimal("-77.384"));
1231     assert(rounded.fixedPoint == Decimal(x.fixedPoint));
1232 }
1233 
1234 //exp
1235 unittest {
1236     string e = "2.71828182845904523536028747135266249775724709369995";
1237     assert(exp(Decimal(1, 18)).toFullString() == e[0 .. 18+2]);
1238     assert(exp(Decimal(0.5, 6)).toFullString() == "1.648721");
1239     assert(exp(Decimal(1, 3)) == Decimal(2.718));
1240     assert(exp(Decimal(1, 2)) == Decimal(2.72));
1241     assert(exp(Decimal(1, 1)) == Decimal(2.7));
1242     assert(exp(Decimal(1, 0)) == Decimal(3));
1243 }
1244 
1245 //log and ln
1246 unittest {
1247     string e = "2.71828182845904523536028747135266249775724709369995";
1248     assert(ln(Decimal(e)).toFullString == "1.000000000000000000");
1249     assert(ln(Decimal(3)).toFullString == "1.098612288668109691");
1250     assert(ln(Decimal(3, 9)) == "1.098612289".D);
1251     assert(ln(Decimal(3, 5)).toFullString == "1.09861");
1252     assert(ln(Decimal(3, 4)).toFullString == "1.0986");
1253     assert(ln(Decimal(3, 3)).toFullString == "1.099");
1254     assert(ln(Decimal(3, 2)).toFullString == "1.10");
1255     assert(ln(Decimal(3, 1)).toFullString == "1.1");
1256     assert(ln(Decimal(3, 0)).toFullString == "1");
1257     assert(format!"%.8D"(ln(Decimal(e, 5))) == "1.00000000");
1258     assert(format!"%.8D"(ln(Decimal(e, 1))) == "1.00000000");
1259 
1260     assert(log(100.D, 10.D) == 2.D);
1261 }
1262 
1263 //pow
1264 unittest {
1265     auto x = Decimal(2);
1266     assert(pow(x, 2) == 4.D);
1267     assert(pow(3.D, 3) == 27.D);
1268     assert(pow(2.D, 0.1.D) == "1.071773462536293164".D);
1269     auto pi = Decimal(
1270         "3.1415926535897932384626433832795028841971693993751" ~
1271         "05820974944592307816406286208998628034825342117",
1272         96 //precision
1273     );
1274     auto piSquared = pow(pi, 2);
1275     auto expectedResult = Decimal(
1276         "9.86960440108935861883449099987615113531369940724" ~
1277         "0790626413349376220044822419205243001773403718552",
1278         96
1279     );
1280     assert(piSquared == expectedResult);
1281     assert(piSquared.fixedPoint == expectedResult.fixedPoint);
1282 
1283     //overloading ^^
1284     assert(2.D ^^ 3.D == 8.D);
1285     assert(2.D ^^ 3 == 8.D);
1286     assert(2 ^^ 3.D == 8.D);
1287     assert(Decimal(2.0, 2) ^^ Decimal(0.1, 18) == "1.071773462536293164".D);
1288     assert(Decimal(2.0, 2) ^^ Decimal(0.1, 16) == "1.0717734625362932".D);
1289     assert(2.D ^^ 0.1.D == "1.071773462536293164".D);
1290     assert(2.D ^^ 0.1 == "1.071773462536293164".D);
1291     assert(2.0 ^^ 0.1.D == "1.071773462536293164".D);
1292 
1293     auto a = Decimal(4.0, 2);
1294     auto b = Decimal(2.0, 3);
1295     auto z = a ^^ b;
1296     assert(z.fixedPoint == 3);
1297     assert(z == Decimal(16));
1298 
1299     a = Decimal(4, 0);
1300     b = Decimal(2, 0);
1301     z = a ^^ b;
1302     assert(z.fixedPoint == 0);
1303     assert(z == Decimal(16));
1304 
1305     a = Decimal(4.5, 1);
1306     b = Decimal(5.25, 1);
1307     z = a ^^ b;
1308     assert(z == Decimal("2492.9"));
1309 
1310     a = Decimal(4.5, 1);
1311     b = Decimal(5.25, 2);
1312     z = a ^^ b;
1313     assert(z == Decimal(2687.61));
1314 
1315     // ^^=
1316     a = Decimal(4.5, 2);
1317     b = Decimal(5.25, 2);
1318     a ^^= b;
1319     assert(a == Decimal(2687.61));
1320 
1321     a = Decimal(4.5, 3);
1322     b = Decimal(5.25, 3);
1323     a ^^= b;
1324     assert(a == Decimal("2687.607"));
1325 }
1326