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