1 /// Multiple precision rational numbers (Q).
2 module gmp.q;
3 
4 import gmp.traits;
5 import gmp.z;
6 
7 import std.algorithm.mutation : move, moveEmplace;
8 
9 /** Arbitrary (multi) precision rational number (Q).
10     Wrapper for GNU MP (GMP)'s type `mpq_t` and functions `__gmpq_.*`.
11  */
12 struct MpQ
13 {
14     pure nothrow pragma(inline, true):
15 
16     /// Convert to `string` in base `base`.
17     string toString(uint base = defaultBase,
18                     bool upperCaseDigits = false) const @trusted
19     {
20         assert((base >= -2 && base <= -36) ||
21                (base >= 2 && base <= 62));
22 
23         assert(false, "TODO");
24     }
25 
26     // TODO toRCString wrapped in UniqueRange
27 
28     /// Returns: A unique hash of the `MpQ` value suitable for use in a hash table.
29     size_t toHash() const
30     {
31         assert(false, "TODO");
32     }
33 
34     @nogc:
35 
36     /** No default construction for now, because `mpq_init` initialize
37         `__mpq_struct`-fields to non-zero values.
38 
39         TODO Allow default construction by delaying call to initialize().
40     */
41     @disable this();
42 
43     /// No copy construction.
44     @disable this(this);
45 
46     /// Construct empty (undefined) from explicit `null`.
47     this(typeof(null)) @safe
48     {
49         initialize();
50     }
51 
52     /** Construct from floating-point `value`.
53      */
54     this(P)(P value) @safe
55         if (isFloating!P)
56     {
57         initialize();
58         this = value;           // reuse opAssign
59     }
60 
61     /** Construct from `pValue` / `qValue`.
62 
63         Note that `qValue` must be explicitly given, to prevent accidental
64         storage of integers as rations with denominator being 1.
65      */
66     this(P, Q)(P pValue, Q qValue,
67                bool canonicalizeFlag = false) @trusted
68         if (isIntegral!P &&
69             isIntegral!Q)
70     {
71         initialize();
72 
73         version(ccc) ++_ccc;
74 
75         static if (isSigned!Q)
76         {
77             assert(qValue >= 1, "Negative denominator");
78         }
79 
80         // dln("qValue:", qValue);
81 
82         static      if (isUnsigned!P)
83         {
84             // dln("unsigned pValue:", pValue);
85             __gmpq_set_ui(_ptr, pValue, qValue);
86         }
87         else                    // signed integral
88         {
89             // dln("signed pValue:", pValue);
90             __gmpq_set_si(_ptr, pValue, qValue);
91         }
92 
93         if (canonicalizeFlag) { canonicalize(); }
94     }
95 
96     /** Construct from floating-point `value`.
97      */
98     ref MpQ opAssign(P)(P value) @trusted /*TODO scope*/
99         if (isFloating!P)
100     {
101         version(ccc) ++_ccc;
102         __gmpq_set_d(_ptr, value);
103         return this;
104     }
105 
106     /** Assign from integer `value`. */
107     ref MpQ opAssign(P)(P value) @trusted /*TODO scope*/
108         if (isIntegral!P)
109     {
110         version(ccc) ++_ccc;
111 
112         static      if (isUnsigned!P)
113             __gmpq_set_ui(_ptr, value, 1);
114         else                    // signed integral
115             __gmpq_set_si(_ptr, value, 1);
116 
117         return this;
118     }
119 
120     /** Canonicalize `this`. */
121     void canonicalize() @trusted
122     {
123         __gmpq_canonicalize(_ptr); version(ccc) ++_ccc;
124     }
125 
126     /// Destruct `this`.
127     ~this() @trusted
128     {
129         assert(_ptr, "Pointer is null");
130         __gmpq_clear(_ptr); version(ccc) ++_ccc;
131     }
132 
133     /// Returns: `true` iff `this` equals `rhs`.
134     bool opEquals()(auto ref const MpQ rhs) const @trusted
135     {
136         if (_ptr == rhs._ptr)   // fast equality
137         {
138             return true;        // fast bailout
139         }
140         return __gmpq_equal(_ptr, rhs._ptr) != 0;
141     }
142     /// ditto
143     int opEquals(T)(T rhs) const @safe
144         if (isIntegral!T)
145     {
146         if (rhs == 0)
147         {
148             return numerator.isZero; // optimization
149         }
150         return numerator == rhs && denominator == 1;
151     }
152 
153     /// Compare `this` to `rhs`.
154     int opCmp()(auto ref const MpQ rhs) const @trusted
155     {
156         if (rhs.numerator == 0)
157         {
158             return sgn;         // optimization
159         }
160         return __gmpq_cmp(_ptr, rhs._ptr);
161     }
162     /// Compare `this` to `rhs`.
163     int opCmp()(auto ref const MpZ rhs) const @trusted
164     {
165         if (rhs == 0)
166         {
167             return sgn;         // optimization
168         }
169         return __gmpq_cmp_z(_ptr,
170                             cast(const(__mpz_struct)*)&rhs); // TODO wrap cast?
171     }
172     /// ditto
173     int opCmp(T)(T rhs) const @trusted
174         if (isIntegral!T)
175     {
176         if (rhs == 0)
177         {
178             return sgn;         // optimization
179         }
180         static if (isUnsigned!T)
181         {
182             return __gmpq_cmp_ui(_ptr, rhs, 1UL);
183         }
184         else                    // isSigned integral
185         {
186             return __gmpq_cmp_si(_ptr, rhs, 1UL);
187         }
188     }
189 
190     /// Returns: numerator reference of `this`.
191     @property ref inout(MpZ) numerator() @trusted inout return /*TODO scope*/
192     {
193         return *(cast(inout(MpZ)*)_num_ptr);
194     }
195 
196     /// Returns: denominator reference of `this`.
197     @property ref inout(MpZ) denominator() @trusted inout return /*TODO scope*/
198     {
199         return *(cast(inout(MpZ)*)_den_ptr);
200     }
201 
202     /// Returns: the integer part of `this`, with any remainder truncated.
203     @property MpZ integerPart() @safe
204     {
205         return numerator / denominator;
206     }
207 
208     /// Returns: the fractional part of `this`.
209     // TODO activate when sub(MpQ, MpZ) has been added
210     // @property MpQ fractionPart()
211     // {
212     //     return this - integerPart;
213     // }
214 
215     /// Cast to arithmetic type `T`.
216     T opCast(T)() const @trusted /*TODO scope*/
217         if (isFloating!T)
218     {
219         return cast(T)__gmpq_get_d(_ptr);
220     }
221 
222     /** Invert `this` in-place.
223         Returns: `void` to make it obvious that `this` is mutated.
224     */
225     void invert() @trusted
226     {
227         import std.algorithm.mutation : swap;
228         const bool negative = numerator < 0;
229         if (negative)
230         {
231             numerator.absolute();         // fast inline
232             swap(numerator, denominator); // fast inline
233             numerator.negate();           // fast inline
234         }
235         else
236         {
237             swap(numerator, denominator); // fast inline
238         }
239     }
240 
241     /** Returns: sign as either
242         - -1 (`this` < 0),
243         -  0 (`this` == 0), or
244         - +1 (`this` > 0).
245     */
246     @property int sgn() const @safe
247     {
248         assert(denominator >= 1);
249         return numerator.sgn;   // sign always stored in numerator so reuse fast
250     }
251 
252     /** Make `this` the absolute value of itself in-place.
253         Returns: `void` to make it obvious that `this` is mutated.
254     */
255     void absolute() @safe
256     {
257         numerator.absolute();
258     }
259 
260     MpQ opBinary(string s)(auto ref const MpQ rhs) const @trusted // direct value
261         if ((s == "+" || s == "-" ||
262              s == "*" || s == "/"))
263     {
264         static if (!__traits(isRef, rhs)) // r-value `rhs`
265         {
266             MpQ* mut_rhs = (cast(MpQ*)(&rhs)); // @trusted because `MpQ` has no aliased indirections
267             static      if (s == "+")
268             {
269                 __gmpq_add(mut_rhs._ptr, _ptr, rhs._ptr); version(ccc) ++mut_rhs._ccc;
270             }
271             else static if (s == "-")
272             {
273                 __gmpq_sub(mut_rhs._ptr, _ptr, rhs._ptr); version(ccc) ++mut_rhs._ccc;
274             }
275             else static if (s == "*")
276             {
277                 __gmpq_mul(mut_rhs._ptr, _ptr, rhs._ptr); version(ccc) ++mut_rhs._ccc;
278             }
279             else static if (s == "/")
280             {
281                 assert(rhs != 0, "Divison by zero");
282                 __gmpq_div(mut_rhs._ptr, _ptr, rhs._ptr); version(ccc) ++mut_rhs._ccc;
283             }
284             else
285             {
286                 static assert(false);
287             }
288             return move(*mut_rhs); // TODO shouldn't have to call `move` here
289         }
290         else
291         {
292             typeof(return) y = null;
293             static      if (s == "+")
294             {
295                 __gmpq_add(y._ptr, _ptr, rhs._ptr); version(ccc) ++y._ccc;
296             }
297             else static if (s == "-")
298             {
299                 __gmpq_sub(y._ptr, _ptr, rhs._ptr); version(ccc) ++y._ccc;
300             }
301             else static if (s == "*")
302             {
303                 __gmpq_mul(y._ptr, _ptr, rhs._ptr); version(ccc) ++y._ccc;
304             }
305             else static if (s == "/")
306             {
307                 assert(rhs != 0, "Divison by zero");
308                 __gmpq_div(y._ptr, _ptr, rhs._ptr); version(ccc) ++y._ccc;
309             }
310             else
311             {
312                 static assert(false);
313             }
314             return y;
315         }
316     }
317 
318     // /// Divided integral with `this`.
319     // Unqual!Lhs opBinaryRight(string s, Lhs)(Lhs lhs) const @trusted
320     //     if ((s == "/") &&
321     //         isIntegral!Lhs)
322     // {
323     //     if (lhs == 1)
324     //     {
325     //         return inv(this);
326     //     }
327     //     else
328     //     {
329     //         MpQ y = null; // TODO avoid if !__traits(isRef, this)
330     //         version(ccc) ++y._ccc;
331     //         assert(this != 0, "Divison by zero");
332     //         denominator *= lhs;
333     //         __gmpq_div(y._ptr, MpZ(lhs)._ptr, _ptr);
334     //         return y;
335     //     }
336     // }
337 
338 private:
339 
340     /// Default conversion base.
341     enum defaultBase = 10;
342 
343     /** Initialize internal struct. */
344     private void initialize() @trusted // cannot be called `init` as that will override builtin type property
345     {
346         __gmpq_init(_ptr); version(ccc) ++_ccc;
347     }
348 
349     /// Returns: pointer to internal rational C struct.
350     inout(__mpq_struct)* _ptr() inout return @system /*TODO scope*/
351     {
352         return &_q;
353     }
354 
355     /// Returns: pointer to internal numerator C struct.
356     inout(__mpz_struct)* _num_ptr() inout return @system /*TODO scope*/
357     {
358         return cast(typeof(return))&_q._mp_num;
359     }
360 
361     /// Returns: pointer to internal denominator C struct.
362     inout(__mpz_struct)* _den_ptr() inout return @system /*TODO scope*/
363     {
364         return cast(typeof(return))&_q._mp_den;
365     }
366 
367     __mpq_struct _q;            // internal libgmp C struct
368 
369     version(ccc)
370     {
371 
372         /** Number of calls made to `__gmpq`--functions that construct or changes
373             this value. Used to verify correct lowering and evaluation of template
374             expressions.
375 
376             For instance the `x` in `x = y + z` should be assigned only once inside
377             a call to `mpq_add`.
378         */
379         @property size_t mutatingCallCount() const @safe { return _ccc; }
380         size_t _ccc;  // C mutation call count. number of calls to C GMP function calls that mutate this object
381     }
382 }
383 
384 pure nothrow pragma(inline, true):
385 
386 /// Swap contents of `x` with contents of `y`.
387 void swap()(ref MpQ x,
388             ref MpQ y)
389 {
390     import std.algorithm.mutation : swap;
391     swap(x, y); // x.swap(y);
392 }
393 
394 /// Returns: absolute value of `x`.
395 MpQ abs()(auto ref const MpQ x) @trusted
396 {
397     static if (__traits(isRef, x)) // l-value `x`
398     {
399         MpQ y = null;
400         __gmpq_abs(y._ptr, x._ptr);
401         return y;
402     }
403     else                        // r-value `x`
404     {
405         MpQ* mut_x = (cast(MpQ*)(&x)); // @trusted because `MpQ` has no aliased indirections
406         mut_x.absolute();
407         return move(*mut_x);    // TODO shouldn't have to call `move` here
408     }
409 }
410 
411 /// Returns: inverse of `x`.
412 MpQ inverse()(auto ref const MpQ x) @trusted
413 {
414     static if (__traits(isRef, x)) // l-value `x`
415     {
416         MpQ y = null;
417         __gmpq_inv(y._ptr, x._ptr);
418         return y;
419     }
420     else                        // r-value `x`
421     {
422         MpQ* mut_x = (cast(MpQ*)(&x)); // @trusted because `MpQ` has no aliased indirections
423         mut_x.invert();
424         return move(*mut_x);    // TODO shouldn't have to call `move` here
425     }
426 }
427 alias inv = inverse;
428 
429 /// construction and assignment
430 @safe @nogc unittest
431 {
432     Q x = null;
433     assert(x.numerator == 0);
434     assert(x.denominator == 1);
435 
436     const Q y = Q(11, 13UL);
437     assert(y.numerator == 11);
438     assert(y.denominator == 13);
439 
440     immutable Q z = Q(7UL, 13UL);
441     assert(z.numerator == 7);
442     assert(z.denominator == 13);
443 
444     Q w = 0.25;                 // construct from `double`
445     assert(w.numerator == 1);
446     assert(w.denominator == 4);
447 
448     w = 0.125f;                 // assign from `float`
449     assert(w.numerator == 1);
450     assert(w.denominator == 8);
451 
452     w = 2;                      // assign from `int`
453     assert(w.numerator == 2);
454     assert(w.denominator == 1);
455 
456     w = 3;                      // assign from `int`
457     assert(w.numerator == 3);
458     assert(w.denominator == 1);
459 }
460 
461 /// canonicalization
462 @safe @nogc unittest
463 {
464     Q x = Q(2, 4);
465     assert(x.numerator == 2);
466     assert(x.denominator == 4);
467     x.canonicalize();
468     assert(x.numerator == 1);
469     assert(x.denominator == 2);
470 }
471 
472 /// negative numerator canonicalization
473 @safe @nogc unittest
474 {
475     Q x = Q(-2, 4);
476     assert(x.numerator == -2);
477     assert(x.denominator == 4);
478     x.canonicalize();
479     assert(x.numerator == -1);
480     assert(x.denominator == 2);
481 }
482 
483 /// swap
484 @safe @nogc unittest
485 {
486     Q x = Q(1, 2);
487     Q y = Q(1, 3);
488     swap(x, y);
489     assert(x == Q(1, 3));
490     assert(y == Q(1, 2));
491 }
492 
493 /// invert
494 @safe unittest
495 {
496     Q x = Q(1, 2);
497     assert(x.numerator == 1);
498     assert(x.denominator == 2);
499 
500     x.invert();
501     assert(x.numerator == 2);
502     assert(x.denominator == 1);
503 
504     Q y = Q(-1, 2);
505 
506     // TODO:
507     // assert(x.numerator == -1);
508     // assert(x.denominator == 2);
509 
510     // x.invert();
511     // assert(x.numerator == -2);
512     // assert(x.denominator == 1);
513 }
514 
515 /// inversion
516 @safe unittest
517 {
518     const Q q = Q(-2, 3);
519     assert(inverse(q) == Q(-3, 2));
520 
521     assert(inverse(Q(2, 3)) == Q(3, 2));
522     assert(inverse(Q(1, 10)) == 10);
523     assert(inverse(Q(10, 1)) == Q(1, 10));
524 }
525 
526 /// absolute value
527 @safe unittest
528 {
529     const Q q = Q(-2, 3);
530     assert(abs(q) == Q(2, 3));
531     assert(abs(Q(-2, 3)) == Q(2, 3));
532 }
533 
534 /// integer and fractional part
535 @safe unittest
536 {
537     Q x = Q(5, 2);
538 
539     assert(x.integerPart == 2);
540     // TODO assert(x.fractionalPart == Q(1, 2));
541 
542     x = Q(7, 2);
543     assert(x.integerPart == 3);
544     // TODO assert(x.fractionalPart == Q(1, 2));
545 
546     x = Q(10, 2);
547     assert(x.integerPart == 5);
548     // TODO assert(x.fractionalPart == 0);
549 
550     x = Q(11, 3);
551     assert(x.integerPart == 3);
552     // TODO assert(x.fractionalPart == Q(2, 3));
553 
554     x = Q(12, 2);
555     assert(x.integerPart == 6);
556     // TODO assert(x.fractionalPart == 0);
557 }
558 
559 /// casting
560 @safe @nogc unittest
561 {
562     assert(cast(double)Q(1, 2) == 0.5f);
563     assert(cast(double)Q(1, 2) == 0.5);
564     assert(cast(double)Q(2, 4) == 0.5);
565     assert(cast(double)Q(1, 8) == 1.0/8);
566 }
567 
568 /// equality
569 @safe unittest
570 {
571     assert(Q(1, 1) == 1);
572     assert(Q(2, 1) == 2);
573     assert(Q(1, 1) == Q(1, 1));
574     assert(Q(1, 1) != Q(1, 2));
575     const x = Q(1, 3);
576     assert(x == x);             // same
577 }
578 
579 /// sign
580 @safe unittest
581 {
582     assert(Q(-1, 3).sgn == -1);
583     assert(Q( 0, 3).sgn ==  0);
584     assert(Q( 1, 3).sgn ==  1);
585 }
586 
587 /// comparison
588 @safe unittest
589 {
590     assert(Q( 1, 3) < Q(1, 2));
591     assert(Q( 1, 2) > Q(1, 3));
592     assert(Q( 1, 2) > Q(0, 1));
593     assert(Q( 0, 1) == Q(0, 1));
594     assert(Q( 0, 2) == Q(0, 1));
595     assert(Q(-1, 2) < Q(0, 1));
596 
597     assert(Q( 1, 3) < 1);
598     assert(Q( 1, 3) > 0);
599     assert(Q(-1, 3) < 0);
600 
601     assert(Q( 1, 3) < 1UL);
602     assert(Q( 1, 3) > 0UL);
603     assert(Q(-1, 3) < 0UL);
604 
605     assert(Q( 1, 3) < 1.Z);
606     assert(Q( 1, 3) > 0.Z);
607     assert(Q(-1, 3) < 0.Z);
608 }
609 
610 /// addition
611 @safe unittest
612 {
613     assert(Q(1, 2) + Q(1, 2) == Q(1, 1));
614     assert(Q(1, 3) + Q(1, 3) == Q(2, 3));
615     assert(Q(1, 2) + Q(1, 3) == Q(5, 6));
616 }
617 
618 /// subtraction
619 @safe unittest
620 {
621     assert(Q(1, 2) - Q(1, 2) == Q( 0, 1));
622     assert(Q(1, 2) - Q(1, 3) == Q (1, 6));
623     assert(Q(1, 3) - Q(1, 2) == Q(-1, 6));
624 }
625 
626 /// multiplication
627 @safe unittest
628 {
629     assert(Q(1, 2) * Q(1, 2) == Q(1, 4));
630     assert(Q(2, 3) * Q(2, 3) == Q(4, 9));
631     assert(Q(1, 2) * Q(1, 3) == Q(1, 6));
632 }
633 
634 /// division
635 @safe unittest
636 {
637     assert(Q(2, 3) / Q(2, 3) == Q(1, 1));
638     assert(Q(2, 3) / Q(2, 3) == 1);
639     assert(Q(2, 3) / Q(3, 2) == Q(4, 9));
640     assert(Q(3, 2) / Q(2, 3) == Q(9, 4));
641     // TODO assert(1 / Q(2, 3) == Q(3, 2));
642 }
643 
644 version(unittest)
645 {
646     // import dbgio : dln;
647     alias Z = MpZ;
648     alias Q = MpQ;
649     version = ccc;              // do C mutation call count
650     static assert(!isMpZExpr!int);
651     import std.meta : AliasSeq;
652 }
653 
654 // C API
655 extern(C) pragma(inline, false)
656 {
657     struct __mpq_struct
658     {
659         __mpz_struct _mp_num;
660         __mpz_struct _mp_den;
661     }
662     static assert(__mpq_struct.sizeof == 32); // fits in four 64-bit words
663 
664     alias mpq_srcptr = const(__mpq_struct)*;
665     alias mpq_ptr = __mpq_struct*;
666     alias mp_bitcnt_t = ulong;
667 
668     pure nothrow @nogc:
669 
670     void __gmpq_init (mpq_ptr);
671     void __gmpq_clear (mpq_ptr);
672 
673     void __gmpq_set (mpq_ptr, mpq_srcptr);
674     void __gmpq_set_z (mpq_ptr, mpz_srcptr);
675 
676     void __gmpq_set_ui (mpq_ptr, ulong, ulong);
677     void __gmpq_set_si (mpq_ptr, long, ulong);
678     void __gmpq_set_d (mpq_ptr, double);
679 
680     double __gmpq_get_d (mpq_srcptr);
681 
682     void __gmpq_canonicalize (mpq_ptr);
683 
684     int __gmpq_equal (mpq_srcptr, mpq_srcptr);
685     int __gmpq_cmp (mpq_srcptr, mpq_srcptr);
686     int __gmpq_cmp_z (mpq_srcptr, mpz_srcptr);
687 
688     int __gmpq_cmp_ui (mpq_srcptr, ulong, ulong);
689     int __gmpq_cmp_si (mpq_srcptr, long, ulong);
690 
691     void __gmpq_add (mpq_ptr, mpq_srcptr, mpq_srcptr);
692     void __gmpq_sub (mpq_ptr, mpq_srcptr, mpq_srcptr);
693     void __gmpq_mul (mpq_ptr, mpq_srcptr, mpq_srcptr);
694     void __gmpq_div (mpq_ptr, mpq_srcptr, mpq_srcptr);
695 
696     void __gmpq_abs (mpq_ptr, mpq_srcptr);
697     void __gmpq_inv (mpq_ptr, mpq_srcptr);
698     }
699 
700 pragma(lib, "gmp");