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 }