1 /** This module contains an implementation of rational numbers that is templated 2 * on the underlying integer type. It can be used with either builtin fixed 3 * width integers or arbitrary precision integers. All relevant operators are 4 * overloaded for both rational-rational and rational-integer operations. 5 * 6 * Synopsis: 7 * --- 8 * // Compute pi using the generalized continued fraction approximation. 9 * import std.bigint, std.rational, std.stdio; 10 * 11 * enum maxTerm = 30; 12 * 13 * Rational!(BigInt) getTerm(int termNumber) 14 * { 15 * auto addFactor = 2 * termNumber - 1; 16 * 17 * if (termNumber == maxTerm) 18 * { 19 * return rational(BigInt(addFactor)); 20 * } 21 * 22 * auto termNumberSquared = BigInt(termNumber * termNumber); 23 * auto continued = termNumberSquared / getTerm(termNumber + 1); 24 * 25 * continued += addFactor; 26 * return continued; 27 * } 28 * 29 * void main() 30 * { 31 * auto pi = rational(BigInt(4)) / getTerm(1); 32 * 33 * // Display the result in rational form. 34 * writeln(pi); 35 * 36 * // Display the decimal equivalent, which is accurate to 18 decimal places. 37 * writefln("%.18f", cast(real) pi); 38 * } 39 * --- 40 * 41 * 42 * Author: David Simcha 43 * Copyright: Copyright (c) 2009-2011, David Simcha. 44 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) 45 */ 46 module nxt.rational; 47 48 import std.conv : to; 49 import std.math : abs; 50 51 /** Checks whether $(D T) is structurally an integer, i.e. whether it supports 52 * all of the operations an integer type should support. Does not check the 53 * nominal type of $(D T). In particular, for a mutable type $(D T) the 54 * following must compile: 55 * 56 * --- 57 * T n; 58 * n = 2; 59 * n <<= 1; 60 * n >>= 1; 61 * n += n; 62 * n += 2; 63 * n *= n; 64 * n *= 2; 65 * n /= n; 66 * n /= 2; 67 * n -= n; 68 * n -= 2; 69 * n %= 2; 70 * n %= n; 71 * bool foo = n < 2; 72 * bool bar = n == 2; 73 * bool goo = n < n + 1; 74 * bool tar = n == n; 75 * --- 76 * 77 * while for a non-mutable type, the above must compile for its unqualified, 78 * mutable variant. 79 * 80 * All built-in D integers and character types and $(XREF bigint, BigInt) are 81 * integer-like by this definition. 82 */ 83 template isIntegerLike(T) 84 { 85 import std.traits : isMutable; 86 static if (isMutable!T) 87 { 88 import std.traits : isIntegral, isSomeChar, isArray, isFloatingPoint; 89 static if (isIntegral!T || 90 isSomeChar!T) 91 { 92 enum isIntegerLike = true; 93 } 94 else static if (isFloatingPoint!T || 95 is(T == bool) || 96 isArray!T) 97 { 98 enum isIntegerLike = false; 99 } 100 else 101 { 102 enum isIntegerLike = is(typeof( 103 { 104 T n; 105 n = 2; 106 n = n; 107 n <<= 1; 108 n >>= 1; 109 n += n; 110 n += 2; 111 n *= n; 112 n *= 2; 113 n /= n; 114 n /= 2; 115 n -= n; 116 n -= 2; 117 n %= 2; 118 n %= n; 119 // TODO: what about ^^= ? 120 bool lt = n < 2; // less than 121 bool eq = n == 2; // equal to literal 122 bool ltg = n < n + 1; 123 bool seq = n == n; // reflexive equal 124 return n; 125 })); 126 } 127 } 128 else 129 { 130 import core.internal.traits : Unqual; 131 alias isIntegerLike = isIntegerLike!(Unqual!T); 132 } 133 } 134 135 @safe pure nothrow @nogc unittest 136 { 137 import std.bigint : BigInt; 138 import std.meta : AliasSeq; 139 foreach (T; AliasSeq!(BigInt, 140 long, ulong, int, uint, 141 short, ushort, byte, ubyte, 142 char, wchar, dchar)) 143 { 144 static assert(isIntegerLike!T); 145 static assert(isIntegerLike!(const(T))); 146 static assert(isIntegerLike!(immutable(T))); 147 } 148 149 foreach (T; AliasSeq!(real, double, float, bool)) 150 { 151 static assert(!isIntegerLike!T); 152 static assert(!isIntegerLike!(const(T))); 153 static assert(!isIntegerLike!(immutable(T))); 154 } 155 } 156 157 /** Checks if $(D T) has the basic properties of a rational type, i.e. it has a 158 * numerator and a denominator. 159 */ 160 enum isRational(T) = (is(typeof(T.init.numerator)) && // TODO: faster to use hasMember? TODO: check that member `isIntegerLike`? 161 is(typeof(T.init.denominator))); // TODO: faster to use hasMember? TODO: check that member `isIntegerLike`? 162 163 /** Returns a Common Integral Type between $(D I1) and $(D I2). This is defined 164 * as the type returned by I1.init * I2.init. 165 */ 166 private template CommonInteger(I1, I2) 167 if (isIntegerLike!I1 && 168 isIntegerLike!I2) 169 { 170 import core.internal.traits : Unqual; 171 alias CommonInteger = typeof(Unqual!(I1).init * 172 Unqual!(I2).init); 173 } 174 175 @safe pure nothrow @nogc unittest 176 { 177 import std.bigint : BigInt; 178 static assert(is(CommonInteger!(BigInt, int) == BigInt)); 179 static assert(is(CommonInteger!(byte, int) == int)); 180 } 181 182 /** Returns a Common Rational Type between $(D R1) and $(D R2), which will be a 183 * Rational based on the CommonInteger of their underlying integer types (or 184 * just on the CommonInteger of ($D R1) and $(D R2), if they themselves are 185 * integers). 186 */ 187 private template CommonRational(R1, R2) // TODO: avoid recursions below 188 { 189 static if (isRational!R1) 190 { 191 alias CommonRational = CommonRational!(typeof(R1.numerator), R2); // recurse. TODO: avoid 192 } 193 else static if (isRational!R2) 194 { 195 alias CommonRational = CommonRational!(R1, typeof(R2.numerator)); // recurse. TODO: avoid 196 } 197 else static if (is(CommonInteger!(R1, R2))) 198 { 199 alias CommonRational = Rational!(CommonInteger!(R1, R2)); 200 } 201 } 202 203 /** Implements rational numbers on top of whatever integer type is specified by 204 * the user. The integer type used may be any type that behaves as an integer. 205 * Specifically, $(D isIntegerLike) must return true, the integer type must have 206 * value semantics, and the semantics of all integer operations must follow the 207 * normal rules of integer arithmetic. 208 * 209 * A regular integer can be converted to rational type simply by passing it as 210 * a single argument. In this case the denominator will simply be set to 1. 211 * 212 * Examples: 213 * --- 214 * auto r1 = rational(BigInt("314159265"), BigInt("27182818")); 215 * auto r2 = rational(BigInt("8675309"), BigInt("362436")); 216 * r1 += r2; 217 * assert(r1 == rational(BigInt("174840986505151"), 218 * BigInt("4926015912324"))); 219 * 220 * // Print result. Prints: 221 * // "174840986505151/4926015912324" 222 * writeln(r1); 223 * 224 * // Print result in decimal form. Prints: 225 * // "35.4934" 226 * writeln(cast(real) r1); 227 * 228 * auto r3 = rational(10); 229 * assert(r3.numerator == 10); 230 * assert(r3.denominator == 1); 231 * assert(r3 == 10); 232 * --- 233 */ 234 Rational!(CommonInteger!(I1, I2)) rational(I1, I2)(I1 i1, I2 i2) 235 if (isIntegerLike!I1 && 236 isIntegerLike!I2) 237 { 238 static if (is(typeof(typeof(return)(i1, i2)))) 239 { 240 // Avoid initializing and then reassigning. 241 auto ret = typeof(return)(i1, i2); 242 } 243 else 244 { 245 /* Don't want to use void initialization b/c BigInts probably use 246 * assignment operator, copy c'tor, etc. 247 */ 248 typeof(return) ret; 249 ret._num = i1; 250 ret._den = i2; 251 } 252 ret.simplify(); 253 return ret; 254 } 255 /// ditto 256 Rational!(I) rational(I)(I val) 257 if (isIntegerLike!I) 258 { 259 return rational(val, 1); 260 } 261 262 /** The struct that implements rational numbers. All relevant operators 263 * (addition, subtraction, multiplication, division, exponentiation by a 264 * non-negative integer, equality and comparison) are overloaded. The second 265 * operand for all binary operators except exponentiation may be either another 266 * $(D Rational) or another integer type. 267 */ 268 struct Rational(SomeIntegral) 269 if (isIntegerLike!SomeIntegral) 270 { 271 public: 272 273 // ----------------Multiplication operators---------------------------------- 274 Rational!(SomeIntegral) opBinary(string op, Rhs)(const scope Rhs that) const 275 if (op == "*" && is(CommonRational!(SomeIntegral, Rhs)) && isRational!Rhs) 276 { 277 auto ret = CommonRational!(SomeIntegral, Rhs)(this.numerator, this.denominator); 278 return ret *= that; 279 } 280 /// ditto 281 Rational!(SomeIntegral) opBinary(string op, Rhs)(const scope Rhs that) const 282 if (op == "*" && is(CommonRational!(SomeIntegral, Rhs)) && isIntegerLike!Rhs) 283 { 284 const factor = gcf(this._den, that); 285 const adjusted_den = cast(SomeIntegral)(this._den / factor); 286 const adjusted_rhs = that / factor; 287 const long new_den = this._num * adjusted_rhs; 288 if (new_den > SomeIntegral.max) 289 throw new Exception(""); 290 else 291 return typeof(this)(cast(SomeIntegral)new_den, 292 adjusted_den); 293 } 294 295 auto opBinaryRight(string op, Rhs)(const scope Rhs that) const 296 if (op == "*" && is(CommonRational!(SomeIntegral, Rhs)) && isIntegerLike!Rhs) 297 { 298 return opBinary!(op, Rhs)(that); // commutative 299 } 300 301 typeof(this) opOpAssign(string op, Rhs)(const scope Rhs that) 302 if (op == "*" && isRational!Rhs) 303 { 304 /* Cancel common factors first, then multiply. This prevents 305 * overflows and is much more efficient when using BigInts. */ 306 auto divisor = gcf(this._num, that._den); 307 this._num /= divisor; 308 const rhs_den = that._den / divisor; 309 310 divisor = gcf(this._den, that._num); // reuse divisor 311 this._den /= divisor; 312 const rhs_num = that._num / divisor; 313 314 this._num *= rhs_num; 315 this._den *= rhs_den; 316 317 /* no simplify. already cancelled common factors before multiplying. */ 318 fixSigns(); 319 320 return this; 321 } 322 /// ditto 323 typeof(this) opOpAssign(string op, Rhs)(const scope Rhs that) 324 if (op == "*" && isIntegerLike!Rhs) 325 { 326 const divisor = gcf(this._den, that); 327 this._den /= divisor; 328 this._num *= that / divisor; 329 /* no to simplify. already cancelled common factors before multiplying. */ 330 fixSigns(); 331 return this; 332 } 333 334 typeof(this) opBinary(string op, Rhs)(const scope Rhs that) const 335 if (op == "/" && 336 is(CommonRational!(int, Rhs)) && 337 isRational!Rhs) 338 { 339 return typeof(return)(_num * that._den, 340 _den * that._num); 341 } 342 // ditto 343 typeof(this) opBinary(string op, Rhs)(const scope Rhs that) const 344 if (op == "/" && 345 is(CommonRational!(int, Rhs)) && 346 isIntegerLike!(Rhs)) 347 { 348 auto ret = CommonRational!(int, Rhs)(this.numerator, this.denominator); 349 return ret /= that; 350 } 351 352 typeof(this) opBinaryRight(string op, Rhs)(const scope Rhs that) const 353 if (op == "/" && 354 is(CommonRational!(int, Rhs)) && 355 isIntegerLike!Rhs) 356 { 357 auto ret = CommonRational!(int, Rhs)(this.denominator, this.numerator); 358 return ret *= that; 359 } 360 361 typeof(this) opOpAssign(string op, Rhs)(const scope Rhs that) 362 if (op == "/" && 363 isIntegerLike!Rhs) 364 { 365 const divisor = gcf(this._num, that); 366 this._num /= divisor; 367 this._den *= that / divisor; 368 369 /* no to simplify. already cancelled common factors before multiplying. */ 370 fixSigns(); 371 return this; 372 } 373 374 typeof(this) opOpAssign(string op, Rhs)(const scope Rhs that) 375 if (op == "/" && 376 isRational!Rhs) 377 { 378 return this *= that.inverse(); 379 } 380 381 // ---------------------addition operators------------------------------------- 382 383 auto opBinary(string op, Rhs)(const scope Rhs that) const 384 if (op == "+" && 385 (isRational!Rhs || 386 isIntegerLike!Rhs)) 387 { 388 auto ret = CommonRational!(typeof(this), Rhs)(this.numerator, this.denominator); 389 return ret += that; 390 } 391 392 auto opBinaryRight(string op, Rhs)(const scope Rhs that) const 393 if (op == "+" && 394 is(CommonRational!(SomeIntegral, Rhs)) && 395 isIntegerLike!Rhs) 396 { 397 return opBinary!(op, Rhs)(that); // commutative 398 } 399 400 typeof(this) opOpAssign(string op, Rhs)(const scope Rhs that) 401 if (op == "+" && 402 isRational!Rhs) 403 { 404 if (this._den == that._den) 405 { 406 this._num += that._num; 407 simplify(); 408 return this; 409 } 410 411 SomeIntegral commonDenom = lcm(this._den, that._den); 412 this._num *= commonDenom / this._den; 413 this._num += (commonDenom / that._den) * that._num; 414 this._den = commonDenom; 415 416 simplify(); 417 return this; 418 } 419 420 typeof(this) opOpAssign(string op, Rhs)(const scope Rhs that) 421 if (op == "+" && 422 isIntegerLike!Rhs) 423 { 424 this._num += that * this._den; 425 426 simplify(); 427 return this; 428 } 429 430 // -----------------------Subtraction operators------------------------------- 431 auto opBinary(string op, Rhs)(const scope Rhs that) const 432 if (op == "-" && 433 is(CommonRational!(SomeIntegral, Rhs))) 434 { 435 auto ret = CommonRational!(typeof(this), Rhs)(this.numerator, 436 this.denominator); 437 return ret -= that; 438 } 439 440 typeof(this) opOpAssign(string op, Rhs)(const scope Rhs that) 441 if (op == "-" && 442 isRational!Rhs) 443 { 444 if (this._den == that._den) 445 { 446 this._num -= that._num; 447 simplify(); 448 return this; 449 } 450 451 auto commonDenom = lcm(this._den, that._den); 452 this._num *= commonDenom / this._den; 453 this._num -= (commonDenom / that._den) * that._num; 454 this._den = commonDenom; 455 456 simplify(); 457 return this; 458 } 459 460 typeof(this) opOpAssign(string op, Rhs)(const scope Rhs that) 461 if (op == "-" && 462 isIntegerLike!Rhs) 463 { 464 this._num -= that * this._den; 465 466 simplify(); 467 return this; 468 } 469 470 typeof(this) opBinaryRight(string op, Rhs)(const scope Rhs that) const 471 if (op == "-" && 472 is(CommonInteger!(SomeIntegral, Rhs)) && 473 isIntegerLike!Rhs) 474 { 475 Rational!(SomeIntegral) ret; 476 ret._den = this._den; 477 ret._num = (that * this._den) - this._num; 478 479 ret.simplify(); 480 return ret; 481 } 482 483 // ----------------------Unary operators--------------------------------------- 484 typeof(this) opUnary(string op)() const 485 if (op == "-" || op == "+") 486 { 487 mixin("return typeof(this)(" ~ op ~ "_num, _den);"); 488 } 489 490 // Can only handle integer powers if the result has to also be rational. 491 typeof(this) opOpAssign(string op, Rhs)(const scope Rhs that) 492 if (op == "^^" && 493 isIntegerLike!Rhs) 494 { 495 if (that < 0) 496 { 497 this.invert(); 498 _num ^^= that * -1; 499 _den ^^= that * -1; 500 } 501 else 502 { 503 _num ^^= that; 504 _den ^^= that; 505 } 506 507 /* don't need to simplify here. this is already simplified, meaning 508 * the numerator and denominator don't have any common factors. raising 509 * both to a positive integer power won't create any. 510 */ 511 return this; 512 } 513 /// ditto 514 auto opBinary(string op, Rhs)(const scope Rhs that) const 515 if (op == "^^" && 516 isIntegerLike!Rhs && 517 is(CommonRational!(SomeIntegral, Rhs))) 518 { 519 auto ret = CommonRational!(SomeIntegral, Rhs)(this.numerator, this.denominator); 520 ret ^^= that; 521 return ret; 522 } 523 524 import std.traits : isAssignable; 525 526 typeof(this) opAssign(Rhs)(const scope Rhs that) 527 if (isIntegerLike!Rhs && 528 isAssignable!(SomeIntegral, Rhs)) 529 { 530 this._num = that; 531 this._den = 1; 532 return this; 533 } 534 535 typeof(this) opAssign(Rhs)(const scope Rhs that) 536 if (isRational!Rhs && 537 isAssignable!(SomeIntegral, typeof(Rhs.numerator))) 538 { 539 this._num = that.numerator; 540 this._den = that.denominator; 541 return this; 542 } 543 544 bool opEquals(Rhs)(const scope Rhs that) const 545 if (isRational!Rhs || 546 isIntegerLike!Rhs) 547 { 548 static if (isRational!Rhs) 549 { 550 return (that._num == this._num && 551 that._den == this._den); 552 } 553 else 554 { 555 static assert(isIntegerLike!Rhs); 556 return (that == this._num && 557 this._den == 1); 558 } 559 } 560 561 int opCmp(Rhs)(const scope Rhs that) const 562 if (isRational!Rhs) 563 { 564 if (opEquals(that)) 565 return 0; 566 567 /* Check a few obvious cases first, see if we can avoid having to use a 568 * common denominator. These are basically speed hacks. 569 * 570 * Assumption: When simplify() is called, rational will be written in 571 * canonical form, with any negative signs being only in the numerator. 572 */ 573 if (this._num < 0 && 574 that._num > 0) 575 { 576 return -1; 577 } 578 else if (this._num > 0 && 579 that._num < 0) 580 { 581 return 1; 582 } 583 else if (this._num >= that._num && 584 this._den <= that._den) 585 { 586 // We've already ruled out equality, so this must be > that. 587 return 1; 588 } 589 else if (that._num >= this._num && 590 that._den <= this._den) 591 { 592 return -1; 593 } 594 595 // Can't do it without common denominator. Argh. 596 auto commonDenom = lcm(this._den, that._den); 597 auto lhsNum = this._num * (commonDenom / this._den); 598 auto rhsNum = that._num * (commonDenom / that._den); 599 600 if (lhsNum > rhsNum) 601 return 1; 602 else if (lhsNum < rhsNum) 603 return -1; 604 605 /* We've checked for equality already. If we get to this point, 606 * there's clearly something wrong. 607 */ 608 assert(0); 609 } 610 611 int opCmp(Rhs)(const scope Rhs that) const 612 if (isIntegerLike!Rhs) 613 { 614 if (opEquals(that)) 615 return 0; 616 617 // Again, check the obvious cases first. 618 if (that >= this._num) 619 return -1; 620 621 const that_ = that * this._den; 622 if (that_ > this._num) 623 return -1; 624 else if (that_ < this._num) 625 return 1; 626 627 // Already checked for equality. If we get here, something's wrong. 628 assert(0); 629 } 630 631 /////////////////////////////////////////////////////////////////////////////// 632 633 /// Get inverse. 634 typeof(this) inverse() const 635 { 636 return typeof(return)(_den, _num); 637 } 638 639 /// Invert `this`. 640 void invert() 641 { 642 import std.algorithm.mutation : swap; 643 swap(_den, _num); 644 } 645 646 import std.traits : isFloatingPoint; 647 648 ///Convert to floating point representation. 649 F opCast(F)() const 650 if (isFloatingPoint!F) 651 { 652 import std.traits : isIntegral; 653 // Do everything in real precision, then convert to F at the end. 654 static if (isIntegral!(SomeIntegral)) 655 { 656 return cast(real) _num / _den; 657 } 658 else 659 { 660 Rational!SomeIntegral temp = this; 661 real expon = 1.0; 662 real ans = 0; 663 byte sign = 1; 664 if (temp._num < 0) 665 { 666 temp._num *= -1; 667 sign = -1; 668 } 669 670 while (temp._num > 0) 671 { 672 while (temp._num < temp._den) 673 { 674 675 assert(temp._den > 0); 676 677 static if (is(typeof(temp._den & 1))) 678 { 679 // Try to make numbers smaller instead of bigger. 680 if ((temp._den & 1) == 0) 681 temp._den >>= 1; 682 else 683 temp._num <<= 1; 684 } 685 else 686 { 687 temp._num <<= 1; 688 } 689 690 expon *= 0.5; 691 692 /* This checks for overflow in case we're working with a 693 * user-defined fixed-precision integer. 694 */ 695 import std.exception : enforce; 696 import std.conv : text; 697 enforce(temp._num > 0, text( 698 "Overflow while converting ", typeof(this).stringof, 699 " to ", F.stringof, ".")); 700 701 } 702 703 auto intPart = temp._num / temp._den; 704 705 static if (is(SomeIntegral == struct) || 706 is(SomeIntegral == class)) 707 { 708 import std.bigint : BigInt; 709 } 710 static if (is(SomeIntegral == BigInt)) 711 { 712 /* This should really be a cast, but BigInt still has a few 713 * issues. 714 */ 715 long lIntPart = intPart.toLong(); 716 } 717 else 718 { 719 long lIntPart = cast(long)intPart; 720 } 721 722 // Test for changes. 723 real oldAns = ans; 724 ans += lIntPart * expon; 725 if (ans == oldAns) // Smaller than epsilon. 726 return ans * sign; 727 728 // Subtract out int part. 729 temp._num -= intPart * temp._den; 730 } 731 732 return ans * sign; 733 } 734 } 735 736 /** Casts $(D this) to an integer by truncating the fractional part. 737 * Equivalent to $(D integerPart), and then casting it to type $(D I). 738 */ 739 I opCast(I)() const 740 if (isIntegerLike!I && 741 is(typeof(cast(I) SomeIntegral.init))) 742 { 743 return cast(I) integerPart; 744 } 745 746 ///Returns the numerator. 747 @property inout(SomeIntegral) numerator() inout 748 { 749 return _num; 750 } 751 752 ///Returns the denominator. 753 @property inout(SomeIntegral) denominator() inout 754 { 755 return _den; 756 } 757 758 /// Returns the integer part of this rational, with any remainder truncated. 759 @property SomeIntegral integerPart() const 760 { 761 return this.numerator / this.denominator; 762 } 763 764 /// Returns the fractional part of this rational. 765 @property typeof(this) fractionPart() const 766 { 767 return this - integerPart; 768 } 769 770 /// Returns a string representation of $(D this) in the form a/b. 771 string toString() const 772 { 773 import std.bigint : BigInt, toDecimalString; 774 static if (is(SomeIntegral == BigInt)) 775 { 776 // Special case it for now. This should be fixed later. 777 return toDecimalString(_num) ~ "/" ~ toDecimalString(_den); 778 } 779 else 780 { 781 return to!string(_num) ~ "/" ~ to!string(_den); 782 } 783 } 784 785 private: 786 SomeIntegral _num; ///< Numerator. 787 SomeIntegral _den; ///< Denominator. 788 789 void simplify() 790 { 791 if (_num == 0) 792 { 793 _den = 1; 794 return; 795 } 796 797 const divisor = gcf(_num, _den); 798 _num /= divisor; 799 _den /= divisor; 800 801 fixSigns(); 802 } 803 804 void fixSigns() scope 805 { 806 static if (!is(SomeIntegral == ulong) && 807 !is(SomeIntegral == uint) && 808 !is(SomeIntegral == ushort) && 809 !is(SomeIntegral == ubyte)) 810 { 811 // Write in canonical form w.r.t. signs. 812 if (_den < 0) 813 { 814 _den *= -1; 815 _num *= -1; 816 } 817 } 818 } 819 } 820 821 class OverflowException : Exception 822 { 823 @safe pure nothrow @nogc: 824 this(string msg) { super(msg); } 825 } 826 827 pure unittest 828 { 829 import std.bigint : BigInt; 830 import std.math : isClose; 831 832 // All reference values from the Maxima computer algebra system. 833 // Test c'tor and simplification first. 834 auto _num = BigInt("295147905179352825852"); 835 auto _den = BigInt("147573952589676412920"); 836 auto simpNum = BigInt("24595658764946068821"); 837 auto simpDen = BigInt("12297829382473034410"); 838 auto f1 = rational(_num, _den); 839 auto f2 = rational(simpNum, simpDen); 840 assert(f1 == f2); 841 // Check that signs of numerator/denominator are corrected 842 assert(rational(10, -3).numerator == -10); 843 assert(rational(7, -5).denominator == 5); 844 845 // Test multiplication. 846 assert((rational(0, 1) * rational(1, 1)) == 0); 847 assert(rational(8, 42) * rational(cast(byte) 7, cast(byte) 68) 848 == rational(1, 51)); 849 assert(rational(20_000L, 3_486_784_401U) * rational(3_486_784_401U, 1_000U) 850 == rational(20, 1)); 851 auto f3 = rational(7, 57); 852 f3 *= rational(2, 78); 853 assert(f3 == rational(7, 2223)); 854 f3 = 5 * f3; 855 assert(f3 == rational(35, 2223)); 856 assert(f3 * 5UL == 5 * f3); 857 858 /* Test division. Since it's implemented in terms of multiplication, 859 * quick and dirty tests should be good enough. 860 */ 861 assert(rational(7, 38) / rational(8, 79) == rational(553, 304)); 862 assert(rational(7, 38) / rational(8, 79) == rational(553, 304)); 863 auto f4 = rational(7, 38); 864 f4 /= rational(8UL, 79); 865 assert(f4 == rational(553, 304)); 866 f4 = f4 / 2; 867 assert(f4 == rational(553, 608)); 868 f4 = 2 / f4; 869 assert(f4 == rational(1216, 553)); 870 assert(f4 * 2 == f4 * rational(2)); 871 f4 = 2; 872 assert(f4 == 2); 873 874 // Test addition. 875 assert(rational(1, 3) + rational(cast(byte) 2, cast(byte) 3) == rational(1, 1)); 876 assert(rational(1, 3) + rational(1, 2L) == rational(5, 6)); 877 auto f5 = rational( BigInt("314159265"), BigInt("27182818")); 878 auto f6 = rational( BigInt("8675309"), BigInt("362436")); 879 f5 += f6; 880 assert(f5 == rational( BigInt("174840986505151"), BigInt("4926015912324"))); 881 assert(rational(1, 3) + 2UL == rational(7, 3)); 882 assert(5UL + rational(1, 5) == rational(26, 5)); 883 884 // Test subtraction. 885 assert(rational(2, 3) - rational(1, 3) == rational(1, 3UL)); 886 assert(rational(1UL, 2) - rational(1, 3) == rational(1, 6)); 887 f5 = rational( BigInt("314159265"), BigInt("27182818")); 888 f5 -= f6; 889 assert(f5 == rational( BigInt("-60978359135611"), BigInt("4926015912324"))); 890 assert(rational(4, 3) - 1 == rational(1, 3)); 891 assert(1 - rational(1, 4) == rational(3, 4)); 892 893 // Test unary operators. 894 auto fExp = rational(2, 5); 895 assert(-fExp == rational(-2, 5)); 896 assert(+fExp == rational(2, 5)); 897 898 // Test exponentiation. 899 fExp ^^= 3; 900 assert(fExp == rational(8, 125)); 901 fExp = fExp ^^ 2; 902 assert(fExp == rational(64, 125 * 125)); 903 assert(rational(2, 5) ^^ -2 == rational(25, 4)); 904 905 // Test decimal conversion. 906 assert(isClose(cast(real) f5, -12.37883925284411L)); 907 908 // Test comparison. 909 assert(rational(1UL, 6) < rational(1, 2)); 910 assert(rational(cast(byte) 1, cast(byte) 2) > rational(1, 6)); 911 assert(rational(-1, 7) < rational(7, 2)); 912 assert(rational(7, 2) > rational(-1, 7)); 913 assert(rational(7, 9) > rational(8, 11)); 914 assert(rational(8, 11) < rational(7, 9)); 915 916 assert(rational(9, 10) < 1UL); 917 assert(1UL > rational(9, 10)); 918 assert(10 > rational(9L, 10)); 919 assert(2 > rational(5, 4)); 920 assert(1 < rational(5U, 4)); 921 922 // Test creating rationals of value zero. 923 auto zero = rational(0, 8); 924 assert(zero == 0); 925 assert(zero == rational(0, 16)); 926 assert(zero.numerator == 0); 927 assert(zero.denominator == 1); 928 auto one = zero + 1; 929 one -= one; 930 assert(one == zero); 931 932 // Test integerPart, fraction part. 933 auto intFract = rational(5, 4); 934 assert(intFract.integerPart == 1); 935 assert(intFract.fractionPart == rational(1, 4)); 936 assert(cast(long) intFract == 1); 937 938 // Test whether CTFE works for primitive types. Doesn't work yet. 939 version(none) 940 { 941 enum myRational = (((rational(1, 2) + rational(1, 4)) * 2 - rational(1, 4)) 942 / 2 + 1 * rational(1, 2) - 1) / rational(2, 5); 943 import std.stdio; 944 writeln(myRational); 945 static assert(myRational == rational(-15, 32)); 946 } 947 } 948 949 /** Convert a floating point number to a $(D Rational) based on integer type $(D 950 * SomeIntegral). Allows an error tolerance of $(D epsilon). (Default $(D epsilon) = 951 * 1e-8.) 952 * 953 * $(D epsilon) must be greater than 1.0L / long.max. 954 * 955 * Throws: Exception on infinities, NaNs, numbers with absolute value 956 * larger than long.max and epsilons smaller than 1.0L / long.max. 957 * 958 * Examples: 959 * --- 960 * // Prints "22/7". 961 * writeln(toRational!int(PI, 1e-1)); 962 * --- 963 */ 964 Rational!(SomeIntegral) toRational(SomeIntegral)(real floatNum, real epsilon = 1e-8) 965 { 966 import std.math: isNaN; 967 import std.exception : enforce; 968 enforce(floatNum != real.infinity && 969 floatNum != -real.infinity && 970 !isNaN(floatNum), 971 "Can't convert NaNs and infinities to rational."); 972 enforce(floatNum < long.max && 973 floatNum > -long.max, 974 "Rational conversions of very large numbers not yet implemented."); 975 enforce(1.0L / epsilon < long.max, 976 "Can't handle very small epsilons < long.max in toRational."); 977 978 /* Handle this as a special case to make the rest of the code less 979 * complicated: 980 */ 981 if (abs(floatNum) < epsilon) 982 { 983 Rational!SomeIntegral ret; 984 ret._num = 0; 985 ret._den = 1; 986 return ret; 987 } 988 989 return toRationalImpl!(SomeIntegral)(floatNum, epsilon); 990 } 991 992 private Rational!SomeIntegral toRationalImpl(SomeIntegral)(real floatNum, real epsilon) 993 { 994 import std.conv : roundTo; 995 import std.traits : isIntegral; 996 997 real actualEpsilon; 998 Rational!SomeIntegral ret; 999 1000 if (abs(floatNum) < 1) 1001 { 1002 real invFloatNum = 1.0L / floatNum; 1003 long intPart = roundTo!long(invFloatNum); 1004 actualEpsilon = floatNum - 1.0L / intPart; 1005 1006 static if (isIntegral!(SomeIntegral)) 1007 { 1008 ret._den = cast(SomeIntegral) intPart; 1009 ret._num = cast(SomeIntegral) 1; 1010 } 1011 else 1012 { 1013 ret._den = intPart; 1014 ret._num = 1; 1015 } 1016 } 1017 else 1018 { 1019 long intPart = roundTo!long(floatNum); 1020 actualEpsilon = floatNum - intPart; 1021 1022 static if (isIntegral!(SomeIntegral)) 1023 { 1024 ret._den = cast(SomeIntegral) 1; 1025 ret._num = cast(SomeIntegral) intPart; 1026 } 1027 else 1028 { 1029 ret._den = 1; 1030 ret._num = intPart; 1031 } 1032 } 1033 1034 if (abs(actualEpsilon) <= epsilon) 1035 return ret; 1036 1037 // Else get results from downstream recursions, add them to this result. 1038 return ret + toRationalImpl!(SomeIntegral)(actualEpsilon, epsilon); 1039 } 1040 1041 unittest 1042 { 1043 import std.bigint : BigInt; 1044 import std.math : PI, E; 1045 // Start with simple cases. 1046 assert(toRational!int(0.5) == rational(1, 2)); 1047 assert(toRational!BigInt(0.333333333333333L) == rational(BigInt(1), BigInt(3))); 1048 assert(toRational!int(2.470588235294118) == rational(cast(int) 42, cast(int) 17)); 1049 assert(toRational!long(2.007874015748032) == rational(255L, 127L)); 1050 assert(toRational!int( 3.0L / 7.0L) == rational(3, 7)); 1051 assert(toRational!int( 7.0L / 3.0L) == rational(7, 3)); 1052 1053 // Now for some fun. 1054 real myEpsilon = 1e-8; 1055 auto piRational = toRational!long(PI, myEpsilon); 1056 assert(abs(cast(real) piRational - PI) < myEpsilon); 1057 1058 auto eRational = toRational!long(E, myEpsilon); 1059 assert(abs(cast(real) eRational - E) < myEpsilon); 1060 } 1061 1062 /** Find the Greatest Common Factor (GCF), aka Greatest Common Divisor (GCD), of 1063 * $(D m) and $(D n). 1064 */ 1065 CommonInteger!(I1, I2) gcf(I1, I2)(I1 m, I2 n) 1066 if (isIntegerLike!I1 && 1067 isIntegerLike!I2) 1068 { 1069 static if (is(I1 == const) || is(I1 == immutable) || 1070 is(I2 == const) || is(I2 == immutable)) 1071 { 1072 // Doesn't work with immutable(BigInt). 1073 import core.internal.traits : Unqual; 1074 return gcf!(Unqual!I1, 1075 Unqual!I2)(m, n); 1076 } 1077 else 1078 { 1079 typeof(return) a = abs(m); 1080 typeof(return) b = abs(n); 1081 1082 while (b) 1083 { 1084 auto t = b; 1085 b = a % b; 1086 a = t; 1087 } 1088 1089 return a; 1090 } 1091 } 1092 1093 pure unittest 1094 { 1095 import std.bigint : BigInt; 1096 assert(gcf(0, 0) == 0); 1097 assert(gcf(0, 1) == 1); 1098 assert(gcf(999, 0) == 999); 1099 assert(gcf(to!(immutable(int))(8), to!(const(int))(12)) == 4); 1100 1101 // Values from the Maxima computer algebra system. 1102 assert(gcf(BigInt(314_156_535UL), BigInt(27_182_818_284UL)) == BigInt(3)); 1103 assert(gcf(8675309, 362436) == 1); 1104 assert(gcf(BigInt("8589934596"), BigInt("295147905179352825852")) == 12); 1105 } 1106 1107 /// Find the Least Common Multiple (LCM) of $(D a) and $(D b). 1108 CommonInteger!(I1, I2) lcm(I1, I2)(const scope I1 a, const scope I2 b) 1109 if (isIntegerLike!I1 && 1110 isIntegerLike!I2) 1111 { 1112 const n1 = abs(a); 1113 const n2 = abs(b); 1114 if (n1 == n2) 1115 return n1; 1116 return (n1 / gcf(n1, n2)) * n2; 1117 } 1118 1119 /// Returns the largest integer less than or equal to $(D r). 1120 SomeIntegral floor(SomeIntegral)(const scope Rational!SomeIntegral r) 1121 { 1122 SomeIntegral intPart = r.integerPart; 1123 if (r > 0 || intPart == r) 1124 return intPart; 1125 else 1126 { 1127 intPart -= 1; 1128 return intPart; 1129 } 1130 } 1131 1132 @safe pure nothrow @nogc unittest 1133 { 1134 assert(floor(rational(1, 2)) == 0); 1135 assert(floor(rational(-1, 2)) == -1); 1136 assert(floor(rational(2)) == 2); 1137 assert(floor(rational(-2)) == -2); 1138 assert(floor(rational(-1, 2)) == -1); 1139 } 1140 1141 /// Returns the smallest integer greater than or equal to $(D r). 1142 SomeIntegral ceil(SomeIntegral)(const scope Rational!SomeIntegral r) 1143 { 1144 SomeIntegral intPart = r.integerPart; 1145 if (intPart == r || r < 0) 1146 return intPart; 1147 else 1148 { 1149 intPart += 1; 1150 return intPart; 1151 } 1152 } 1153 1154 @safe pure nothrow @nogc unittest 1155 { 1156 assert(ceil(rational(1, 2)) == 1); 1157 assert(ceil(rational(0)) == 0); 1158 assert(ceil(rational(-1, 2)) == 0); 1159 assert(ceil(rational(1)) == 1); 1160 assert(ceil(rational(-2)) == -2); 1161 } 1162 1163 /** Round $(D r) to the nearest integer. If the fractional part is exactly 1/2, 1164 * $(D r) will be rounded such that the absolute value is increased by rounding. 1165 */ 1166 SomeIntegral round(SomeIntegral)(const scope Rational!SomeIntegral r) 1167 { 1168 auto intPart = r.integerPart; 1169 const fractPart = r.fractionPart; 1170 1171 bool added; 1172 if (fractPart >= rational(1, 2)) 1173 { 1174 added = true; 1175 intPart += 1; 1176 } 1177 1178 import std.traits : isUnsigned; 1179 1180 static if (!isUnsigned!SomeIntegral) 1181 { 1182 if (!added && fractPart <= rational(-1, 2)) 1183 intPart -= 1; 1184 } 1185 1186 return intPart; 1187 } 1188 1189 @safe pure nothrow @nogc unittest 1190 { 1191 assert(round(rational(1, 3)) == 0); 1192 assert(round(rational(7, 2)) == 4); 1193 assert(round(rational(-3, 4)) == -1); 1194 assert(round(rational(8U, 15U)) == 1); 1195 }