1const std = @import("../../std.zig");
2const debug = std.debug;
3const math = std.math;
4const mem = std.mem;
5const testing = std.testing;
6const Allocator = mem.Allocator;
7
8const Limb = std.math.big.Limb;
9const DoubleLimb = std.math.big.DoubleLimb;
10const Int = std.math.big.int.Managed;
11const IntConst = std.math.big.int.Const;
12
13/// An arbitrary-precision rational number.
14///
15/// Memory is allocated as needed for operations to ensure full precision is kept. The precision
16/// of a Rational is only bounded by memory.
17///
18/// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers,
19/// gcd(p, q) = 1 always.
20///
21/// TODO rework this to store its own allocator and use a non-managed big int, to avoid double
22/// allocator storage.
23pub const Rational = struct {
24    /// Numerator. Determines the sign of the Rational.
25    p: Int,
26
27    /// Denominator. Sign is ignored.
28    q: Int,
29
30    /// Create a new Rational. A small amount of memory will be allocated on initialization.
31    /// This will be 2 * Int.default_capacity.
32    pub fn init(a: Allocator) !Rational {
33        return Rational{
34            .p = try Int.init(a),
35            .q = try Int.initSet(a, 1),
36        };
37    }
38
39    /// Frees all memory associated with a Rational.
40    pub fn deinit(self: *Rational) void {
41        self.p.deinit();
42        self.q.deinit();
43    }
44
45    /// Set a Rational from a primitive integer type.
46    pub fn setInt(self: *Rational, a: anytype) !void {
47        try self.p.set(a);
48        try self.q.set(1);
49    }
50
51    /// Set a Rational from a string of the form `A/B` where A and B are base-10 integers.
52    pub fn setFloatString(self: *Rational, str: []const u8) !void {
53        // TODO: Accept a/b fractions and exponent form
54        if (str.len == 0) {
55            return error.InvalidFloatString;
56        }
57
58        const State = enum {
59            Integer,
60            Fractional,
61        };
62
63        var state = State.Integer;
64        var point: ?usize = null;
65
66        var start: usize = 0;
67        if (str[0] == '-') {
68            start += 1;
69        }
70
71        for (str) |c, i| {
72            switch (state) {
73                State.Integer => {
74                    switch (c) {
75                        '.' => {
76                            state = State.Fractional;
77                            point = i;
78                        },
79                        '0'...'9' => {
80                            // okay
81                        },
82                        else => {
83                            return error.InvalidFloatString;
84                        },
85                    }
86                },
87                State.Fractional => {
88                    switch (c) {
89                        '0'...'9' => {
90                            // okay
91                        },
92                        else => {
93                            return error.InvalidFloatString;
94                        },
95                    }
96                },
97            }
98        }
99
100        // TODO: batch the multiplies by 10
101        if (point) |i| {
102            try self.p.setString(10, str[0..i]);
103
104            const base = IntConst{ .limbs = &[_]Limb{10}, .positive = true };
105
106            var j: usize = start;
107            while (j < str.len - i - 1) : (j += 1) {
108                try self.p.ensureMulCapacity(self.p.toConst(), base);
109                try self.p.mul(self.p.toConst(), base);
110            }
111
112            try self.q.setString(10, str[i + 1 ..]);
113            try self.p.add(self.p.toConst(), self.q.toConst());
114
115            try self.q.set(1);
116            var k: usize = i + 1;
117            while (k < str.len) : (k += 1) {
118                try self.q.mul(self.q.toConst(), base);
119            }
120
121            try self.reduce();
122        } else {
123            try self.p.setString(10, str[0..]);
124            try self.q.set(1);
125        }
126    }
127
128    /// Set a Rational from a floating-point value. The rational will have enough precision to
129    /// completely represent the provided float.
130    pub fn setFloat(self: *Rational, comptime T: type, f: T) !void {
131        // Translated from golang.go/src/math/big/rat.go.
132        debug.assert(@typeInfo(T) == .Float);
133
134        const UnsignedInt = std.meta.Int(.unsigned, @typeInfo(T).Float.bits);
135        const f_bits = @bitCast(UnsignedInt, f);
136
137        const exponent_bits = math.floatExponentBits(T);
138        const exponent_bias = (1 << (exponent_bits - 1)) - 1;
139        const mantissa_bits = math.floatMantissaBits(T);
140
141        const exponent_mask = (1 << exponent_bits) - 1;
142        const mantissa_mask = (1 << mantissa_bits) - 1;
143
144        var exponent = @intCast(i16, (f_bits >> mantissa_bits) & exponent_mask);
145        var mantissa = f_bits & mantissa_mask;
146
147        switch (exponent) {
148            exponent_mask => {
149                return error.NonFiniteFloat;
150            },
151            0 => {
152                // denormal
153                exponent -= exponent_bias - 1;
154            },
155            else => {
156                // normal
157                mantissa |= 1 << mantissa_bits;
158                exponent -= exponent_bias;
159            },
160        }
161
162        var shift: i16 = mantissa_bits - exponent;
163
164        // factor out powers of two early from rational
165        while (mantissa & 1 == 0 and shift > 0) {
166            mantissa >>= 1;
167            shift -= 1;
168        }
169
170        try self.p.set(mantissa);
171        self.p.setSign(f >= 0);
172
173        try self.q.set(1);
174        if (shift >= 0) {
175            try self.q.shiftLeft(self.q, @intCast(usize, shift));
176        } else {
177            try self.p.shiftLeft(self.p, @intCast(usize, -shift));
178        }
179
180        try self.reduce();
181    }
182
183    /// Return a floating-point value that is the closest value to a Rational.
184    ///
185    /// The result may not be exact if the Rational is too precise or too large for the
186    /// target type.
187    pub fn toFloat(self: Rational, comptime T: type) !T {
188        // Translated from golang.go/src/math/big/rat.go.
189        // TODO: Indicate whether the result is not exact.
190        debug.assert(@typeInfo(T) == .Float);
191
192        const fsize = @typeInfo(T).Float.bits;
193        const BitReprType = std.meta.Int(.unsigned, fsize);
194
195        const msize = math.floatMantissaBits(T);
196        const msize1 = msize + 1;
197        const msize2 = msize1 + 1;
198
199        const esize = math.floatExponentBits(T);
200        const ebias = (1 << (esize - 1)) - 1;
201        const emin = 1 - ebias;
202
203        if (self.p.eqZero()) {
204            return 0;
205        }
206
207        // 1. left-shift a or sub so that a/b is in [1 << msize1, 1 << (msize2 + 1)]
208        var exp = @intCast(isize, self.p.bitCountTwosComp()) - @intCast(isize, self.q.bitCountTwosComp());
209
210        var a2 = try self.p.clone();
211        defer a2.deinit();
212
213        var b2 = try self.q.clone();
214        defer b2.deinit();
215
216        const shift = msize2 - exp;
217        if (shift >= 0) {
218            try a2.shiftLeft(a2, @intCast(usize, shift));
219        } else {
220            try b2.shiftLeft(b2, @intCast(usize, -shift));
221        }
222
223        // 2. compute quotient and remainder
224        var q = try Int.init(self.p.allocator);
225        defer q.deinit();
226
227        // unused
228        var r = try Int.init(self.p.allocator);
229        defer r.deinit();
230
231        try Int.divTrunc(&q, &r, a2.toConst(), b2.toConst());
232
233        var mantissa = extractLowBits(q, BitReprType);
234        var have_rem = r.len() > 0;
235
236        // 3. q didn't fit in msize2 bits, redo division b2 << 1
237        if (mantissa >> msize2 == 1) {
238            if (mantissa & 1 == 1) {
239                have_rem = true;
240            }
241            mantissa >>= 1;
242            exp += 1;
243        }
244        if (mantissa >> msize1 != 1) {
245            // NOTE: This can be hit if the limb size is small (u8/16).
246            @panic("unexpected bits in result");
247        }
248
249        // 4. Rounding
250        if (emin - msize <= exp and exp <= emin) {
251            // denormal
252            const shift1 = @intCast(math.Log2Int(BitReprType), emin - (exp - 1));
253            const lost_bits = mantissa & ((@intCast(BitReprType, 1) << shift1) - 1);
254            have_rem = have_rem or lost_bits != 0;
255            mantissa >>= shift1;
256            exp = 2 - ebias;
257        }
258
259        // round q using round-half-to-even
260        var exact = !have_rem;
261        if (mantissa & 1 != 0) {
262            exact = false;
263            if (have_rem or (mantissa & 2 != 0)) {
264                mantissa += 1;
265                if (mantissa >= 1 << msize2) {
266                    // 11...1 => 100...0
267                    mantissa >>= 1;
268                    exp += 1;
269                }
270            }
271        }
272        mantissa >>= 1;
273
274        const f = math.scalbn(@intToFloat(T, mantissa), @intCast(i32, exp - msize1));
275        if (math.isInf(f)) {
276            exact = false;
277        }
278
279        return if (self.p.isPositive()) f else -f;
280    }
281
282    /// Set a rational from an integer ratio.
283    pub fn setRatio(self: *Rational, p: anytype, q: anytype) !void {
284        try self.p.set(p);
285        try self.q.set(q);
286
287        self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);
288        self.q.setSign(true);
289
290        try self.reduce();
291
292        if (self.q.eqZero()) {
293            @panic("cannot set rational with denominator = 0");
294        }
295    }
296
297    /// Set a Rational directly from an Int.
298    pub fn copyInt(self: *Rational, a: Int) !void {
299        try self.p.copy(a.toConst());
300        try self.q.set(1);
301    }
302
303    /// Set a Rational directly from a ratio of two Int's.
304    pub fn copyRatio(self: *Rational, a: Int, b: Int) !void {
305        try self.p.copy(a.toConst());
306        try self.q.copy(b.toConst());
307
308        self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0);
309        self.q.setSign(true);
310
311        try self.reduce();
312    }
313
314    /// Make a Rational positive.
315    pub fn abs(r: *Rational) void {
316        r.p.abs();
317    }
318
319    /// Negate the sign of a Rational.
320    pub fn negate(r: *Rational) void {
321        r.p.negate();
322    }
323
324    /// Efficiently swap a Rational with another. This swaps the limb pointers and a full copy is not
325    /// performed. The address of the limbs field will not be the same after this function.
326    pub fn swap(r: *Rational, other: *Rational) void {
327        r.p.swap(&other.p);
328        r.q.swap(&other.q);
329    }
330
331    /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a
332    /// > b respectively.
333    pub fn order(a: Rational, b: Rational) !math.Order {
334        return cmpInternal(a, b, true);
335    }
336
337    /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| ==
338    /// |b| or |a| > |b| respectively.
339    pub fn orderAbs(a: Rational, b: Rational) !math.Order {
340        return cmpInternal(a, b, false);
341    }
342
343    // p/q > x/y iff p*y > x*q
344    fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !math.Order {
345        // TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid
346        // the memory allocations here?
347        var q = try Int.init(a.p.allocator);
348        defer q.deinit();
349
350        var p = try Int.init(b.p.allocator);
351        defer p.deinit();
352
353        try q.mul(a.p.toConst(), b.q.toConst());
354        try p.mul(b.p.toConst(), a.q.toConst());
355
356        return if (is_abs) q.orderAbs(p) else q.order(p);
357    }
358
359    /// rma = a + b.
360    ///
361    /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
362    ///
363    /// Returns an error if memory could not be allocated.
364    pub fn add(rma: *Rational, a: Rational, b: Rational) !void {
365        var r = rma;
366        var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr;
367
368        var sr: Rational = undefined;
369        if (aliased) {
370            sr = try Rational.init(rma.p.allocator);
371            r = &sr;
372            aliased = true;
373        }
374        defer if (aliased) {
375            rma.swap(r);
376            r.deinit();
377        };
378
379        try r.p.mul(a.p.toConst(), b.q.toConst());
380        try r.q.mul(b.p.toConst(), a.q.toConst());
381        try r.p.add(r.p.toConst(), r.q.toConst());
382
383        try r.q.mul(a.q.toConst(), b.q.toConst());
384        try r.reduce();
385    }
386
387    /// rma = a - b.
388    ///
389    /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
390    ///
391    /// Returns an error if memory could not be allocated.
392    pub fn sub(rma: *Rational, a: Rational, b: Rational) !void {
393        var r = rma;
394        var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr;
395
396        var sr: Rational = undefined;
397        if (aliased) {
398            sr = try Rational.init(rma.p.allocator);
399            r = &sr;
400            aliased = true;
401        }
402        defer if (aliased) {
403            rma.swap(r);
404            r.deinit();
405        };
406
407        try r.p.mul(a.p.toConst(), b.q.toConst());
408        try r.q.mul(b.p.toConst(), a.q.toConst());
409        try r.p.sub(r.p.toConst(), r.q.toConst());
410
411        try r.q.mul(a.q.toConst(), b.q.toConst());
412        try r.reduce();
413    }
414
415    /// rma = a * b.
416    ///
417    /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
418    ///
419    /// Returns an error if memory could not be allocated.
420    pub fn mul(r: *Rational, a: Rational, b: Rational) !void {
421        try r.p.mul(a.p.toConst(), b.p.toConst());
422        try r.q.mul(a.q.toConst(), b.q.toConst());
423        try r.reduce();
424    }
425
426    /// rma = a / b.
427    ///
428    /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b.
429    ///
430    /// Returns an error if memory could not be allocated.
431    pub fn div(r: *Rational, a: Rational, b: Rational) !void {
432        if (b.p.eqZero()) {
433            @panic("division by zero");
434        }
435
436        try r.p.mul(a.p.toConst(), b.q.toConst());
437        try r.q.mul(b.p.toConst(), a.q.toConst());
438        try r.reduce();
439    }
440
441    /// Invert the numerator and denominator fields of a Rational. p/q => q/p.
442    pub fn invert(r: *Rational) void {
443        Int.swap(&r.p, &r.q);
444    }
445
446    // reduce r/q such that gcd(r, q) = 1
447    fn reduce(r: *Rational) !void {
448        var a = try Int.init(r.p.allocator);
449        defer a.deinit();
450
451        const sign = r.p.isPositive();
452        r.p.abs();
453        try a.gcd(r.p, r.q);
454        r.p.setSign(sign);
455
456        const one = IntConst{ .limbs = &[_]Limb{1}, .positive = true };
457        if (a.toConst().order(one) != .eq) {
458            var unused = try Int.init(r.p.allocator);
459            defer unused.deinit();
460
461            // TODO: divexact would be useful here
462            // TODO: don't copy r.q for div
463            try Int.divTrunc(&r.p, &unused, r.p.toConst(), a.toConst());
464            try Int.divTrunc(&r.q, &unused, r.q.toConst(), a.toConst());
465        }
466    }
467};
468
469fn extractLowBits(a: Int, comptime T: type) T {
470    debug.assert(@typeInfo(T) == .Int);
471
472    const t_bits = @typeInfo(T).Int.bits;
473    const limb_bits = @typeInfo(Limb).Int.bits;
474    if (t_bits <= limb_bits) {
475        return @truncate(T, a.limbs[0]);
476    } else {
477        var r: T = 0;
478        comptime var i: usize = 0;
479
480        // Remainder is always 0 since if t_bits >= limb_bits -> Limb | T and both
481        // are powers of two.
482        inline while (i < t_bits / limb_bits) : (i += 1) {
483            r |= math.shl(T, a.limbs[i], i * limb_bits);
484        }
485
486        return r;
487    }
488}
489
490test "big.rational extractLowBits" {
491    var a = try Int.initSet(testing.allocator, 0x11112222333344441234567887654321);
492    defer a.deinit();
493
494    const a1 = extractLowBits(a, u8);
495    try testing.expect(a1 == 0x21);
496
497    const a2 = extractLowBits(a, u16);
498    try testing.expect(a2 == 0x4321);
499
500    const a3 = extractLowBits(a, u32);
501    try testing.expect(a3 == 0x87654321);
502
503    const a4 = extractLowBits(a, u64);
504    try testing.expect(a4 == 0x1234567887654321);
505
506    const a5 = extractLowBits(a, u128);
507    try testing.expect(a5 == 0x11112222333344441234567887654321);
508}
509
510test "big.rational set" {
511    var a = try Rational.init(testing.allocator);
512    defer a.deinit();
513
514    try a.setInt(5);
515    try testing.expect((try a.p.to(u32)) == 5);
516    try testing.expect((try a.q.to(u32)) == 1);
517
518    try a.setRatio(7, 3);
519    try testing.expect((try a.p.to(u32)) == 7);
520    try testing.expect((try a.q.to(u32)) == 3);
521
522    try a.setRatio(9, 3);
523    try testing.expect((try a.p.to(i32)) == 3);
524    try testing.expect((try a.q.to(i32)) == 1);
525
526    try a.setRatio(-9, 3);
527    try testing.expect((try a.p.to(i32)) == -3);
528    try testing.expect((try a.q.to(i32)) == 1);
529
530    try a.setRatio(9, -3);
531    try testing.expect((try a.p.to(i32)) == -3);
532    try testing.expect((try a.q.to(i32)) == 1);
533
534    try a.setRatio(-9, -3);
535    try testing.expect((try a.p.to(i32)) == 3);
536    try testing.expect((try a.q.to(i32)) == 1);
537}
538
539test "big.rational setFloat" {
540    var a = try Rational.init(testing.allocator);
541    defer a.deinit();
542
543    try a.setFloat(f64, 2.5);
544    try testing.expect((try a.p.to(i32)) == 5);
545    try testing.expect((try a.q.to(i32)) == 2);
546
547    try a.setFloat(f32, -2.5);
548    try testing.expect((try a.p.to(i32)) == -5);
549    try testing.expect((try a.q.to(i32)) == 2);
550
551    try a.setFloat(f32, 3.141593);
552
553    //                = 3.14159297943115234375
554    try testing.expect((try a.p.to(u32)) == 3294199);
555    try testing.expect((try a.q.to(u32)) == 1048576);
556
557    try a.setFloat(f64, 72.141593120712409172417410926841290461290467124);
558
559    //                = 72.1415931207124145885245525278151035308837890625
560    try testing.expect((try a.p.to(u128)) == 5076513310880537);
561    try testing.expect((try a.q.to(u128)) == 70368744177664);
562}
563
564test "big.rational setFloatString" {
565    var a = try Rational.init(testing.allocator);
566    defer a.deinit();
567
568    try a.setFloatString("72.14159312071241458852455252781510353");
569
570    //                  = 72.1415931207124145885245525278151035308837890625
571    try testing.expect((try a.p.to(u128)) == 7214159312071241458852455252781510353);
572    try testing.expect((try a.q.to(u128)) == 100000000000000000000000000000000000);
573}
574
575test "big.rational toFloat" {
576    var a = try Rational.init(testing.allocator);
577    defer a.deinit();
578
579    // = 3.14159297943115234375
580    try a.setRatio(3294199, 1048576);
581    try testing.expect((try a.toFloat(f64)) == 3.14159297943115234375);
582
583    // = 72.1415931207124145885245525278151035308837890625
584    try a.setRatio(5076513310880537, 70368744177664);
585    try testing.expect((try a.toFloat(f64)) == 72.141593120712409172417410926841290461290467124);
586}
587
588test "big.rational set/to Float round-trip" {
589    var a = try Rational.init(testing.allocator);
590    defer a.deinit();
591    var prng = std.rand.DefaultPrng.init(0x5EED);
592    const random = prng.random();
593    var i: usize = 0;
594    while (i < 512) : (i += 1) {
595        const r = random.float(f64);
596        try a.setFloat(f64, r);
597        try testing.expect((try a.toFloat(f64)) == r);
598    }
599}
600
601test "big.rational copy" {
602    var a = try Rational.init(testing.allocator);
603    defer a.deinit();
604
605    var b = try Int.initSet(testing.allocator, 5);
606    defer b.deinit();
607
608    try a.copyInt(b);
609    try testing.expect((try a.p.to(u32)) == 5);
610    try testing.expect((try a.q.to(u32)) == 1);
611
612    var c = try Int.initSet(testing.allocator, 7);
613    defer c.deinit();
614    var d = try Int.initSet(testing.allocator, 3);
615    defer d.deinit();
616
617    try a.copyRatio(c, d);
618    try testing.expect((try a.p.to(u32)) == 7);
619    try testing.expect((try a.q.to(u32)) == 3);
620
621    var e = try Int.initSet(testing.allocator, 9);
622    defer e.deinit();
623    var f = try Int.initSet(testing.allocator, 3);
624    defer f.deinit();
625
626    try a.copyRatio(e, f);
627    try testing.expect((try a.p.to(u32)) == 3);
628    try testing.expect((try a.q.to(u32)) == 1);
629}
630
631test "big.rational negate" {
632    var a = try Rational.init(testing.allocator);
633    defer a.deinit();
634
635    try a.setInt(-50);
636    try testing.expect((try a.p.to(i32)) == -50);
637    try testing.expect((try a.q.to(i32)) == 1);
638
639    a.negate();
640    try testing.expect((try a.p.to(i32)) == 50);
641    try testing.expect((try a.q.to(i32)) == 1);
642
643    a.negate();
644    try testing.expect((try a.p.to(i32)) == -50);
645    try testing.expect((try a.q.to(i32)) == 1);
646}
647
648test "big.rational abs" {
649    var a = try Rational.init(testing.allocator);
650    defer a.deinit();
651
652    try a.setInt(-50);
653    try testing.expect((try a.p.to(i32)) == -50);
654    try testing.expect((try a.q.to(i32)) == 1);
655
656    a.abs();
657    try testing.expect((try a.p.to(i32)) == 50);
658    try testing.expect((try a.q.to(i32)) == 1);
659
660    a.abs();
661    try testing.expect((try a.p.to(i32)) == 50);
662    try testing.expect((try a.q.to(i32)) == 1);
663}
664
665test "big.rational swap" {
666    var a = try Rational.init(testing.allocator);
667    defer a.deinit();
668    var b = try Rational.init(testing.allocator);
669    defer b.deinit();
670
671    try a.setRatio(50, 23);
672    try b.setRatio(17, 3);
673
674    try testing.expect((try a.p.to(u32)) == 50);
675    try testing.expect((try a.q.to(u32)) == 23);
676
677    try testing.expect((try b.p.to(u32)) == 17);
678    try testing.expect((try b.q.to(u32)) == 3);
679
680    a.swap(&b);
681
682    try testing.expect((try a.p.to(u32)) == 17);
683    try testing.expect((try a.q.to(u32)) == 3);
684
685    try testing.expect((try b.p.to(u32)) == 50);
686    try testing.expect((try b.q.to(u32)) == 23);
687}
688
689test "big.rational order" {
690    var a = try Rational.init(testing.allocator);
691    defer a.deinit();
692    var b = try Rational.init(testing.allocator);
693    defer b.deinit();
694
695    try a.setRatio(500, 231);
696    try b.setRatio(18903, 8584);
697    try testing.expect((try a.order(b)) == .lt);
698
699    try a.setRatio(890, 10);
700    try b.setRatio(89, 1);
701    try testing.expect((try a.order(b)) == .eq);
702}
703
704test "big.rational add single-limb" {
705    var a = try Rational.init(testing.allocator);
706    defer a.deinit();
707    var b = try Rational.init(testing.allocator);
708    defer b.deinit();
709
710    try a.setRatio(500, 231);
711    try b.setRatio(18903, 8584);
712    try testing.expect((try a.order(b)) == .lt);
713
714    try a.setRatio(890, 10);
715    try b.setRatio(89, 1);
716    try testing.expect((try a.order(b)) == .eq);
717}
718
719test "big.rational add" {
720    var a = try Rational.init(testing.allocator);
721    defer a.deinit();
722    var b = try Rational.init(testing.allocator);
723    defer b.deinit();
724    var r = try Rational.init(testing.allocator);
725    defer r.deinit();
726
727    try a.setRatio(78923, 23341);
728    try b.setRatio(123097, 12441414);
729    try a.add(a, b);
730
731    try r.setRatio(984786924199, 290395044174);
732    try testing.expect((try a.order(r)) == .eq);
733}
734
735test "big.rational sub" {
736    var a = try Rational.init(testing.allocator);
737    defer a.deinit();
738    var b = try Rational.init(testing.allocator);
739    defer b.deinit();
740    var r = try Rational.init(testing.allocator);
741    defer r.deinit();
742
743    try a.setRatio(78923, 23341);
744    try b.setRatio(123097, 12441414);
745    try a.sub(a, b);
746
747    try r.setRatio(979040510045, 290395044174);
748    try testing.expect((try a.order(r)) == .eq);
749}
750
751test "big.rational mul" {
752    var a = try Rational.init(testing.allocator);
753    defer a.deinit();
754    var b = try Rational.init(testing.allocator);
755    defer b.deinit();
756    var r = try Rational.init(testing.allocator);
757    defer r.deinit();
758
759    try a.setRatio(78923, 23341);
760    try b.setRatio(123097, 12441414);
761    try a.mul(a, b);
762
763    try r.setRatio(571481443, 17082061422);
764    try testing.expect((try a.order(r)) == .eq);
765}
766
767test "big.rational div" {
768    var a = try Rational.init(testing.allocator);
769    defer a.deinit();
770    var b = try Rational.init(testing.allocator);
771    defer b.deinit();
772    var r = try Rational.init(testing.allocator);
773    defer r.deinit();
774
775    try a.setRatio(78923, 23341);
776    try b.setRatio(123097, 12441414);
777    try a.div(a, b);
778
779    try r.setRatio(75531824394, 221015929);
780    try testing.expect((try a.order(r)) == .eq);
781}
782
783test "big.rational div" {
784    var a = try Rational.init(testing.allocator);
785    defer a.deinit();
786    var r = try Rational.init(testing.allocator);
787    defer r.deinit();
788
789    try a.setRatio(78923, 23341);
790    a.invert();
791
792    try r.setRatio(23341, 78923);
793    try testing.expect((try a.order(r)) == .eq);
794
795    try a.setRatio(-78923, 23341);
796    a.invert();
797
798    try r.setRatio(-23341, 78923);
799    try testing.expect((try a.order(r)) == .eq);
800}
801