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