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