1 // Written in the D programming language. 2 /* 3 NYSL Version 0.9982 4 5 A. This software is "Everyone'sWare". It means: 6 Anybody who has this software can use it as if he/she is 7 the author. 8 9 A-1. Freeware. No fee is required. 10 A-2. You can freely redistribute this software. 11 A-3. You can freely modify this software. And the source 12 may be used in any software with no limitation. 13 A-4. When you release a modified version to public, you 14 must publish it with your name. 15 16 B. The author is not responsible for any kind of damages or loss 17 while using or misusing this software, which is distributed 18 "AS IS". No warranty of any kind is expressed or implied. 19 You use AT YOUR OWN RISK. 20 21 C. Copyrighted to Kazuki KOMATSU 22 23 D. Above three clauses are applied both to source and binary 24 form of this software. 25 */ 26 27 /** 28 このモジュールでは、分数型を扱うことが出来ます。 29 */ 30 module carbon.rational; 31 32 import carbon.traits; 33 34 import std.algorithm, 35 std.array, 36 std.bigint, 37 std.format, 38 std.functional, 39 std.traits; 40 41 /* 42 if T is like int, isLikeInt!T is true. Where "like int" type has operators same of int. 43 44 Example: 45 --- 46 static assert(!isLikeInt!(byte)); 47 static assert(!isLikeInt!(short)); 48 static assert(isLikeInt!(int)); 49 static assert(isLikeInt!(long)); 50 static assert(!isLikeInt!(ubyte)); 51 static assert(!isLikeInt!(ushort)); 52 static assert(isLikeInt!(uint)); 53 static assert(isLikeInt!(ulong)); 54 55 static assert(isLikeInt!(BigInt)); 56 --- 57 */ 58 private 59 enum bool isLikeInt(T) = 60 is(typeof({ 61 T a = 1; 62 a = 0; 63 a = a; 64 65 ++a; 66 --a; 67 a++; 68 a--; 69 a = -a; 70 a = +a; 71 72 a += a; 73 a -= a; 74 a *= a; 75 a /= a; 76 a %= a; 77 //a ^^= a; 78 79 a += 1; 80 a -= 1; 81 a *= 1; 82 a /= 1; 83 a %= 1; 84 a ^^= 1; 85 86 a = a + a; 87 a = a - a; 88 a = a * a; 89 a = a / a; 90 a = a % a; 91 //a = a ^^ a; 92 93 a = a + 1; 94 a = a - 1; 95 a = a * 1; 96 a = a / 1; 97 a = a % 1; 98 a = a ^^ 1; 99 100 bool b = a < 0; 101 b = a == 0; 102 })); 103 unittest{ 104 static assert(!isLikeInt!(byte)); 105 static assert(!isLikeInt!(short)); 106 static assert(isLikeInt!(int)); 107 static assert(isLikeInt!(long)); 108 static assert(!isLikeInt!(ubyte)); 109 static assert(!isLikeInt!(ushort)); 110 static assert(isLikeInt!(uint)); 111 static assert(isLikeInt!(ulong)); 112 113 static assert(isLikeInt!(BigInt)); 114 } 115 116 117 private 118 template isLikeBuiltInInt(T) 119 { 120 alias checkCode = 121 unaryFun!((T a){ 122 T b = 1; 123 b = 0; 124 b = b; 125 126 ++b; 127 --b; 128 b++; 129 b--; 130 b = cast(const)-b; 131 b = cast(const)+b; 132 133 b += cast(const)b; 134 b -= cast(const)b; 135 b *= cast(const)b; 136 b /= cast(const)b; 137 b %= cast(const)b; 138 //b ^^= b; 139 140 b += 1; 141 b -= 1; 142 b *= 1; 143 b /= 1; 144 b %= 1; 145 b ^^= 1; 146 147 b = cast(const)b + cast(const)b; 148 b = cast(const)b - cast(const)b; 149 b = cast(const)b * cast(const)b; 150 b = cast(const)b / cast(const)b; 151 b = cast(const)b % cast(const)b; 152 //b = b ^^ b; 153 154 b = cast(const)b + 1; 155 b = cast(const)b - 1; 156 b = cast(const)b * 1; 157 b = cast(const)b / 1; 158 b = cast(const)b % 1; 159 b = cast(const)b ^^ 1; 160 161 bool c = cast(const)b < 0; 162 c = cast(const)b == 0; 163 }); 164 165 166 enum isLikeBuiltInInt = FuncAttr.isPure!(() => checkCode(T.init)) 167 && FuncAttr.isNothrow!(() => checkCode(T.init)) 168 && isSafe!(() => checkCode(T.init)); 169 } 170 unittest{ 171 static assert(isLikeBuiltInInt!int); 172 static assert(isLikeBuiltInInt!long); 173 } 174 175 176 private 177 auto gcd(T, U)(T a, U b) 178 if(is(typeof(a + b))) 179 { 180 alias C = Unqual!(typeof(a + b)); 181 182 C _a = a < 0 ? a * -1 : a, 183 _b = b < 0 ? b * -1 : b; 184 185 while(_a != 0 && _b != 0){ 186 if(_a > _b) 187 _a %= _b; 188 else 189 _b %= _a; 190 } 191 192 if(_a == 0) 193 return _b; 194 else 195 return _a; 196 } 197 198 199 private 200 auto lcm(T, U)(T a, U b) 201 { 202 return a / gcd(a, b) * b; 203 } 204 205 206 /** 207 This is the type that you can calculate fraction. 208 $(B Rational!T) has two integral $(B T) values. 209 210 Example: 211 --- 212 auto r = rational(10, 2); // If you called rational(n, d), value is reduced. 213 assert(r.num == 5); // 10 / 2 => 5 / 1 214 assert(r.den == 1); 215 216 assert(r == rational(5)); // rational(5) == rational(5, 1) 217 218 assert(r == 5.over(1)); // UFCS : n.over(d) == n.rational(d) == rational(n, d) 219 220 r *= -1.over(5); 221 assert(r.num == -1); // If rational value is negative, numerator is always negative. 222 assert(r.den == 1); // But denominator is always positive. 223 assert(r == rational(1, 1)); // (5 / 1) * (1 / 5) == (1 / 1) 224 assert(r == 1); // Can check equality to T by "==" operator. 225 assert(r > 2); // Also comparison operator. 226 227 r1 = 2.over(5) + 3; // You can use Rational!T like T. 228 229 import std.bigint; 230 Rational!BigInt rb = 10.over(33);// You can use BigInt as T. 231 rb ^^= -10; 232 assert(rb == Rational!BigInt(BigInt(33)^^10, BigInt(10)^^10)); 233 --- 234 235 If $(B T) can be operated in $(B pure nothrow @safe function), 236 $(B Rational!T) can be too. 237 238 Example: 239 ------------------------------------------------------- 240 void foo() pure nothrow @safe 241 { 242 auto r = rational(1, 3); //int is pure nothrow @safe type 243 r += 3.over(4); 244 ... 245 } 246 ------------------------------------------------------- 247 248 You can use $(B "%(...%)") format when formatted write. 249 Where inner format $(B "...") can be $(B T)'s format, first one is numerator's format, second is denominator's format. 250 251 Example: 252 --- 253 import std.format; 254 255 void main(){ 256 auto writer = appender!string; 257 258 formattedWrite(writer, "%(%04d / %04d%)", rational(10, 33)); 259 assert(writer.data == "0010 / 0033"); 260 261 writer = appender!string; 262 formattedWrite(writer, "%(den : %2$s , num : %1$s%)", rational(10, 33)); 263 assert(writer.data == "den : 33 , num : 10"); 264 265 writer = appender!string; 266 formattedWrite(writer, "%04d", rational(10, 30)); 267 assert(writer.data == "0010/0030"); 268 } 269 --- 270 */ 271 struct Rational(T) 272 if(isLikeInt!T && !isFloatingPoint!T) 273 { 274 private: 275 T _num = 0; //分子 276 T _den = 1; //分母 277 278 279 debug(rational) 280 { 281 const invariant() 282 { 283 assert(_den != 0); 284 } 285 } 286 287 288 void reduce() 289 { 290 if(_num == 0){ 291 if(_den < 0) 292 _den = -1; 293 else 294 _den = 1; 295 }else{ 296 auto _gcd = gcd(_num, _den); 297 _num /= _gcd; 298 _den /= _gcd; 299 } 300 301 if(_den < 0){ 302 _num = -_num; 303 _den = -_den; 304 } 305 } 306 307 308 public: 309 version(none) 310 { 311 static typeof(this) init() @property 312 { 313 static if(is(typeof({typeof(this) r = typeof(this)(0, 1);}))) 314 typeof(this) r = typeof(this)(0, 1); 315 else{ 316 typeof(this) r; 317 ++r._den; 318 } 319 return r; 320 } 321 } 322 323 324 ///ditto 325 this(U)(in U n) 326 { 327 _num = n; 328 _den = 1; 329 } 330 331 332 ///ditto 333 this(U, V)(in U n, in V d, bool nonReduce = false) 334 if(isAssignable!(T, const(U)) && isAssignable!(T, const(V))) 335 { 336 _num = n; 337 _den = d; 338 339 if(!nonReduce) reduce(); 340 } 341 342 343 /// numerator 344 @property 345 inout(T) num() inout 346 { 347 return _num; 348 } 349 350 351 /// ditto 352 @property 353 void num(U)(in U u) 354 if(isAssignable!(T, const(U))) 355 { 356 _num = u; 357 reduce(); 358 } 359 360 361 /// denominator 362 @property 363 inout(T) den() inout 364 { 365 return _den; 366 } 367 368 369 /// ditto 370 @property 371 void den(U)(in U u) 372 if(isAssignable!(T, const(U))) 373 in{ 374 assert(u != 0); 375 } 376 body{ 377 _den = u; 378 reduce(); 379 } 380 381 382 /// return reciprocal number 383 @property 384 typeof(this) reciprocal() const 385 { 386 return _num < 0 ? typeof(this)(-_den, -_num, false) : typeof(this)(_den , _num, false); 387 } 388 389 390 /// operator 391 void opAssign(U)(in U v) 392 if(!isRationalType!U && isAssignable!(T, U)) 393 { 394 _den = 1; 395 _num = v; 396 } 397 398 399 /// ditto 400 void opAssign(U)(in Rational!U r) 401 if(isAssignable!(T, U)) 402 { 403 _den = r._den; 404 _num = r._num; 405 } 406 407 408 /// ditto 409 typeof(this) opUnary(string op)() const 410 if(!find(["-", "+"], op).empty) 411 { 412 static if(op == "-") 413 return rational(-_num, _den); 414 else static if(op == "+") 415 return rational(_num, _den); 416 } 417 418 419 /// ditto 420 typeof(this) opUnary(string op)() 421 if(!find(["++", "--"], op).empty) 422 { 423 static if(op == "++") 424 _num += _den; 425 else static if(op == "--") 426 _num -= _den; 427 428 return this; 429 } 430 431 432 ///ditto 433 bool opCast(U : bool)() const 434 { 435 return _num != 0; 436 } 437 438 439 ///ditto 440 U opCast(U : T)() const 441 { 442 return _num / _den; 443 } 444 445 446 /// ditto 447 U opCast(U)() const 448 if(isRationalType!U && is(typeof({auto e = U(_num, _den, true);}))) 449 { 450 return U(_num, _den, true); 451 } 452 453 454 /// ditto 455 U opCast(U)() const 456 if(isRationalType!U && !is(typeof({auto e = U(_num, _den);})) && is(typeof({auto e = cast(typeof(U.init._num))_num;}))) 457 { 458 alias E = typeof(U.init._num); 459 return U(cast(E)_num, cast(E)_den, true); 460 } 461 462 463 /// ditto 464 auto opBinary(string op, U)(in Rational!U r) const 465 if(!find(["+", "-", "*", "/", "%"], op).empty) 466 { 467 static if(op == "+"){ 468 auto gcd1 = gcd(_den, r._den); 469 return rational(_num * (r._den / gcd1) + r._num * (_den / gcd1), _den / gcd1 * r._den); 470 } 471 else static if(op == "-"){ 472 auto gcd1 = gcd(_den, r._den); 473 return rational(_num * (r._den / gcd1) - r._num * (_den / gcd1), _den / gcd1 * r._den); 474 } 475 else static if(op == "*"){ 476 auto gcd1 = gcd(_num, r._den); 477 auto gcd2 = gcd(r._num, _den); 478 return rational((_num/gcd1) * (r._num / gcd2), (_den/gcd2) * (r._den / gcd1), true); 479 } 480 else static if(op == "/"){ 481 auto gcd1 = gcd(_num, r._num); 482 auto gcd2 = gcd(r._den, _den); 483 if(r._num < 0) 484 gcd1 = -gcd1; 485 return rational((_num/gcd1) * (r._den / gcd2), (_den/gcd2) * (r._num / gcd1), true); 486 } 487 else static if(op == "%"){ 488 auto gcd1 = gcd(_den, r._den); 489 return rational((_num * (r._den / gcd1)) % (r._num * (_den / gcd1)), _den / gcd1 * r._den); 490 } 491 } 492 493 494 /// ditto 495 auto opBinary(string op, U)(in U v) const 496 if(!isRationalType!U && isLikeInt!U && !find(["+", "-", "*", "/", "%", "^^"], op).empty) 497 { 498 static if(op == "+") 499 return rational(_num + _den * v, _den); 500 else static if(op == "-") 501 return rational(_num - _den * v, _den); 502 else static if(op == "*") 503 return rational(_num * v, _den); 504 else static if(op == "/") 505 return rational(_num, _den * v); 506 else static if(op == "%") 507 return rational(_num % (v * _den), _den); 508 else static if(op == "^^"){ 509 if(v >= 0) 510 return rational(_num ^^ v, _den ^^ v); 511 else{ 512 if(_num >= 0) 513 return rational(_den ^^ (-v), _num ^^ (-v)); 514 else 515 return rational((-_den) ^^ (-v), (-_num) ^^ (-v)); 516 } 517 }else 518 static assert(0); 519 } 520 521 522 /// ditto 523 auto opBinaryRight(string op, U)(in U v) const 524 if(!isRationalType!U && isLikeInt!U && !find(["+", "-", "*", "/", "%"], op).empty) 525 { 526 static if(op == "+") 527 return rational(_num + _den * v, _den); 528 else static if(op == "-") 529 return rational(_den * v - num, _den); 530 else static if(op == "*") 531 return rational(_num * v, _den); 532 else static if(op == "/") 533 return rational(v * _den, _num); 534 else static if(op == "%") 535 return rational((v * _den) % _num, _den); 536 } 537 538 539 /// ditto 540 void opOpAssign(string op, U)(in Rational!U r) 541 if(!find(["+", "-", "*", "/", "%"], op).empty) 542 in{ 543 static if(op == "/") 544 assert(r._num != 0); 545 } 546 body{ 547 static if(op == "+"){ 548 auto gcd1 = gcd(_den, r._den); 549 _num = _num * (r._den / gcd1) + r._num * (_den / gcd1); 550 _den = _den / gcd1 * r._den; 551 reduce(); 552 } 553 else static if(op == "-"){ 554 auto gcd1 = gcd(_den, r._den); 555 _num = _num * (r._den / gcd1) - r._num * (_den / gcd1); 556 _den = _den / gcd1 * r._den; 557 reduce(); 558 } 559 else static if(op == "*"){ 560 auto gcd1 = gcd(_num, r._den); 561 auto gcd2 = gcd(r._num, _den); 562 _num = (_num / gcd1) * (r._num / gcd2); 563 _den = (_den / gcd2) * (r._den / gcd1); 564 } 565 else static if(op == "/"){ 566 auto gcd1 = gcd(_num, r._num); 567 auto gcd2 = gcd(r._den, _den); 568 569 if(r._num >= 0){ 570 _num = (_num / gcd1) * (r._den / gcd2); 571 _den = (_den / gcd2) * (r._num / gcd1); 572 }else{ 573 _num = -(_num / gcd1) * (r._den / gcd2); 574 _den = -(_den / gcd2) * (r._num / gcd1); 575 } 576 } 577 else static if(op == "%"){ 578 auto gcd1 = gcd(_den, r._den); 579 _num = (_num * (r._den / gcd1)) % (r._num * (_den / gcd1)); 580 _den = _den / gcd1 * r._den; 581 reduce(); 582 } 583 } 584 585 586 /// ditto 587 void opOpAssign(string op, U)(const U v) 588 if(!isRationalType!U && isLikeInt!U && !find(["+", "-", "*", "/", "%", "^^"], op).empty) 589 in{ 590 static if(op == "^^") 591 assert(!(v < 0 && _num == 0)); 592 } 593 body{ 594 static if(op == "+"){ 595 _num += _den * v; 596 }else static if(op == "-"){ 597 _num -= _den * v; 598 }else static if(op == "*"){ 599 _num *= v; 600 reduce(); 601 }else static if(op == "/"){ 602 _den *= v; 603 reduce(); 604 }else static if(op == "%"){ 605 _num %= _den * v; 606 reduce(); 607 }else static if(op == "^^"){ 608 if(v >= 0){ 609 _num ^^= v; 610 _den ^^= v; 611 }else{ 612 if(_num >= 0){ 613 auto tmp = _num; 614 _num = _den ^^ (-v); 615 _den = tmp ^^ (-v); 616 }else{ 617 auto tmp = -_num; 618 _num = (-_den) ^^ (-v); 619 _den = (tmp) ^^ (-v); 620 } 621 } 622 } 623 } 624 625 626 /// ditto 627 auto opCmp(U)(auto ref const U r) const 628 if(!isRationalType!U) 629 { 630 return _num - r * _den; 631 } 632 633 634 /// ditto 635 auto opCmp(U)(auto ref const Rational!U r) const 636 { 637 auto _gcd = gcd(_den, r._den); 638 return (_num * (r._den / _gcd)) - (r._num * (_den / _gcd)); 639 } 640 641 642 /// ditto 643 bool opEquals(U)(auto ref const U v) const 644 if(!isRationalType!U) 645 { 646 return _den == 1 && _num == v; 647 } 648 649 650 /// ditto 651 bool opEquals(U)(auto ref const Rational!U r) const 652 { 653 return (_num == r._num) && (_den == r._den); 654 } 655 656 657 /// ditto 658 void toString(scope void delegate(const(char)[]) sink, FormatSpec!char fmt) const 659 { 660 if(fmt.nested.length != 0){ 661 formattedWrite(sink, fmt.nested, _num, _den); 662 }else{ 663 formatValue(sink, _num, fmt); 664 sink("/"); 665 formatValue(sink, _den, fmt); 666 } 667 } 668 } 669 670 671 ///ditto 672 Rational!(Unqual!(CommonType!(T, U))) rational(T, U)(T num, U den) pure nothrow @safe if(isLikeBuiltInInt!(Unqual!(CommonType!(T, U)))) 673 { 674 return Rational!(Unqual!(CommonType!(T, U)))(num, den, false); 675 } 676 677 678 ///ditto 679 Rational!(Unqual!(CommonType!(T, U))) rational(T, U)(T num, U den) if(!isLikeBuiltInInt!(Unqual!(CommonType!(T, U)))) 680 { 681 return Rational!(Unqual!(CommonType!(T, U)))(num, den, false); 682 } 683 684 685 ///ditto 686 Rational!(Unqual!T) rational(T)(T num) pure nothrow @safe if(isLikeBuiltInInt!(Unqual!T)) 687 { 688 return Rational!(Unqual!T)(num, 1); 689 } 690 691 692 ///ditto 693 Rational!(Unqual!T) rational(T)(T num) if(!isLikeBuiltInInt!(Unqual!T)) 694 { 695 return Rational!(Unqual!T)(num, 1); 696 } 697 698 699 private 700 Rational!(Unqual!(CommonType!(T, U))) rational(T, U)(T num, U den, bool nonReduce) pure nothrow @safe if(isLikeBuiltInInt!(Unqual!(CommonType!(T, U)))) 701 { 702 return Rational!(Unqual!(CommonType!(T, U)))(num, den, nonReduce); 703 } 704 705 706 private 707 Rational!(Unqual!(CommonType!(T, U))) rational(T, U)(T num, U den, bool nonReduce) if(!isLikeBuiltInInt!(Unqual!(CommonType!(T, U)))) 708 { 709 return Rational!(Unqual!(CommonType!(T, U)))(num, den, nonReduce); 710 } 711 712 713 ///ditto 714 alias over = rational; 715 716 717 /// 718 unittest{ 719 import std.stdio; 720 721 static void foo(T)() 722 { 723 alias Rational!T R; 724 alias R r; 725 726 assert(R.init == r(0, 1)); 727 assert(R.init.den != 0); 728 729 assert(r(0, -3) == r(0, 1)); 730 731 static if(isIntegral!T) // int, long 732 static assert(r(2, 15) == r(4, 5) % r(1, 6)); //CTFEable 733 734 assert(3.over(2) == r(3, 2)); //num.over(den) 735 736 //opUnary and cast test 737 assert(-r(5) == r(-5, 1)); 738 assert(+r(5) == r(5)); 739 assert(++r(5, 13) == r(18, 13)); 740 assert(--r(5, 13) == r(-8, 13)); 741 assert(!r(0)); 742 assert(r(1)); 743 assert(cast(T)r(10, 3) == r(10, 3).num / r(10, 3).den); 744 745 //opBinary test 746 assert(r(5, 6) + r(3, 8) == r(29, 24)); 747 assert(r(-1, 3) + r(3, 2) == r(7, 6)); 748 assert(r(1, 3) - r(4, 5) == r(-7, 15)); 749 assert(r(-1, 3) - r(-4, 5) == r(7, 15)); 750 assert(r(5, 6) * r(3, 8) == r(5, 16)); 751 assert(r(-1, 3) * r(3, 2) == r(-1, 2)); 752 assert(r(1, 3) / r(4, 5) == r(5, 12)); 753 assert(r(-1, 3) / r(-4, 5) == r(5, 12)); 754 assert(r(1, 3) % r(4, 5) == r(5, 15)); 755 assert(r(-1, 3) % r(-4, 5) == r(-5, 15)); 756 757 assert(r(5, 6) + 3 == r(23, 6)); 758 assert(r(-1, 3) + 3 == r(8, 3)); 759 assert(r(1, 3) - 3 == r(-8, 3)); 760 assert(r(-1, 3) - 3 == r(-10, 3)); 761 assert(r(5, 6) * 3 == r(5, 2)); 762 assert(r(-1, 3) * 3 == r(-1, 1)); 763 assert(r(1, 3) / 3 == r(1, 9)); 764 assert(r(-1, 3) / 3 == r(-1, 9)); 765 assert(r(1, 3) % 3 == r(1, 3)); 766 assert(r(-1, 3) % 3 == r(-1, 3)); 767 assert(r(2, 3) ^^ 3 == r(8, 27)); 768 assert(r(2, 3) ^^ 4 == r(16, 81)); 769 assert(r(-2, 3) ^^ 3 == -r(8, 27)); 770 assert(r(-2, 3) ^^ 4 == r(16, 81)); 771 assert(r(2, 3) ^^ -3 == r(27, 8)); 772 assert(r(2, 3) ^^ -4 == r(81, 16)); 773 assert(r(-2, 3) ^^ -3 == -r(27, 8)); 774 assert(r(-2, 3) ^^ -4 == r(81, 16)); 775 assert(r(-1, 3) ^^ -3 == r(-27, 1)); 776 777 assert(3 + r(5, 6) == r(23, 6)); 778 assert(3 + r(-1, 3) == r(8, 3)); 779 assert(3 - r(1, 3) == r(8, 3)); 780 assert(3 - r(-1, 3) == r(10, 3)); 781 assert(3 * r(5, 6) == r(5, 2)); 782 assert(3 * r(-1, 3) == r(-1, 1)); 783 assert(3 / r(1, 3) == r(9, 1)); 784 assert(3 / r(-1, 3) == r(-9, 1)); 785 assert(3 % r(2, 3) == r(1, 3)); 786 assert(3 % r(-2, 3) == r(1, 3)); 787 788 { 789 R r1 = 3; 790 assert(r1 == r(3, 1)); 791 } 792 793 auto r1 = r(5, 6); 794 r1 += r(3, 8); 795 assert(r1 == r(29, 24)); 796 r1 += r(3, 2); 797 assert(r1 == r(65, 24)); 798 799 r1 = r(1, 3); 800 r1 -= r(4, 5); 801 assert(r1 == r(-7, 15)); 802 r1 -= r(-4, 5); 803 assert(r1 == r(1, 3)); 804 805 r1 = r(5, 6); 806 r1 *= r(3, 8); 807 assert(r1 == r(5, 16)); 808 r1 *= r(3, 2); 809 assert(r1 == r(15, 32)); 810 811 r1 = r(1, 3); 812 r1 /= r(4, 5); 813 assert(r1 == r(5, 12)); 814 r1 /= r(-4, 5); 815 assert(r1 == r(-25, 48)); 816 817 r1 = r(4, 3); //r(20, 15) 818 r1 %= r(4, 5); //r(12, 15) 819 assert(r1 == r(8, 15)); 820 r1 %= r(-2, 5); //r(-6, 15) 821 assert(r1 == r(2, 15)); 822 823 824 r1 = r(-5, 6); 825 r1 += 3; 826 assert(r1 == r(13, 6)); 827 r1 += -3; 828 assert(r1 == r(-5, 6)); 829 830 r1 = r(-1, 3); 831 r1 -= 3; 832 assert(r1 == r(-10, 3)); 833 r1 -= -3; 834 assert(r1 == r(-1, 3)); 835 836 r1 = r(-5, 6); 837 r1 *= 3; 838 assert(r1 == r(-5, 2)); 839 r1 *= -3; 840 assert(r1 == r(15, 2)); 841 842 r1 = r(-1, 3); 843 r1 /= 4; 844 assert(r1 == r(-1, 12)); 845 r1 /= -4; 846 assert(r1 == r(1, 48)); 847 848 r1 = r(17, 2); //r(51, 6) 849 r1 %= 3; //r(18, 6) 850 assert(r1 == r(5, 2)); //r(25, 10) 851 r1 = r(-25, 10); //r(-25, 10) 852 r1 %= r(2, 5); //r(6, 10) 853 assert(r1 == r(-1, 10)); 854 855 r1 = r(2, 3); 856 r1 ^^= 3; 857 assert(r1 == r(8, 27)); 858 859 r1 = r(2, 3); 860 r1 ^^= 4; 861 assert(r1 == r(16, 81)); 862 863 r1 = -r(2, 3); 864 r1 ^^= 3; 865 assert(r1 == -r(8, 27)); 866 867 r1 = -r(2, 3); 868 r1 ^^= 4; 869 assert(r1 == r(16, 81)); 870 871 r1 = r(2, 3); 872 r1 ^^= -3; 873 assert(r1 == r(27, 8)); 874 875 r1 = r(2, 3); 876 r1 ^^= -4; 877 assert(r1 == r(81, 16)); 878 879 r1 = -r(2, 3); 880 r1 ^^= -3; 881 assert(r1 == -r(27, 8)); 882 883 r1 = -r(2, 3); 884 r1 ^^= -4; 885 assert(r1 == r(81, 16)); 886 887 r1 = r(-1, 3); 888 r1 ^^= 3; 889 assert(r1 == r(-1, 27)); 890 r1 ^^= -2; 891 assert(r1 == r(729, 1)); 892 893 assert(r1 == 729); 894 assert(r1 < 799); 895 assert(r1 < r(700*8, 3)); 896 assert(r1 > r(700*2, 3)); 897 assert(r1 == r(729, 1)); 898 assert(r1.reciprocal == r(1, 729)); 899 } 900 901 foo!int(); 902 foo!long(); 903 foo!BigInt(); 904 905 // CTFE test 906 static assert(is(typeof({ 907 enum bar = { 908 foo!int(); 909 return true; 910 }(); 911 }))); 912 913 static assert(is(typeof({ 914 enum bar = { 915 foo!long(); 916 return true; 917 }(); 918 }))); 919 920 // pure nothrow @safe test 921 static assert(FuncAttr.isPure!(foo!int) 922 && FuncAttr.isNothrow!(foo!int) 923 && std.traits.isSafe!(foo!int)); 924 925 static assert(FuncAttr.isPure!(foo!long) 926 && FuncAttr.isNothrow!(foo!long) 927 && std.traits.isSafe!(foo!long)); 928 929 auto r1 = rational(729, 1); 930 931 auto writer = appender!string; 932 formattedWrite(writer, "%(%08d / %04d%)", r1); 933 assert(writer.data == "00000729 / 0001"); 934 935 writer = appender!string; 936 formattedWrite(writer, "%(%2$s/%1$s%)", r1); 937 assert(writer.data == "1/729"); 938 939 writer = appender!string; 940 formattedWrite(writer, "%08d", r1); 941 assert(writer.data == "00000729/00000001"); 942 943 944 // like literal 945 assert(-1.over(5) == rational(-1, 5)); 946 assert(-1.rational(5) == rational(-1, 5)); 947 } 948 949 950 /** 951 true if T is rational 952 */ 953 template isRationalType(T){ 954 static if(is(T U == Rational!U)) 955 enum bool isRationalType = true; 956 else 957 enum bool isRationalType = false; 958 } 959 960 961 unittest 962 { 963 static assert(isRationalType!(Rational!int)); 964 static assert(isRationalType!(Rational!uint)); 965 static assert(isRationalType!(Rational!long)); 966 static assert(isRationalType!(Rational!ulong)); 967 static assert(isRationalType!(Rational!BigInt)); 968 }