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");