authorgravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-03-26 19:53:02+13:00
committergravatar for marc@tiehu.isMarc Tiehuis <marc@tiehu.is> 2019-04-11 19:36:35+12:00
log5f4fcd5030fa93cf2518f98ffc666e4e4dc1052b
tree1bc60d17234e103f288f2cdc4a7f2ae43fd034a0
parentea1d2a240956b2b25d945d361fd274e67ec7849c

Add initial big.Rational type


3 files changed, 901 insertions(+), 0 deletions(-)

CMakeLists.txt+1
...@@ -521,6 +521,7 @@ set(ZIG_STD_FILES...@@ -521,6 +521,7 @@ set(ZIG_STD_FILES
521 "math/atanh.zig"521 "math/atanh.zig"
522 "math/big.zig"522 "math/big.zig"
523 "math/big/int.zig"523 "math/big/int.zig"
524 "math/big/rational.zig"
524 "math/cbrt.zig"525 "math/cbrt.zig"
525 "math/ceil.zig"526 "math/ceil.zig"
526 "math/complex.zig"527 "math/complex.zig"
std/math/big.zig+2
...@@ -1,5 +1,7 @@...@@ -1,5 +1,7 @@
1pub use @import("big/int.zig");1pub use @import("big/int.zig");
2pub use @import("big/rational.zig");
23
3test "math.big" {4test "math.big" {
4 _ = @import("big/int.zig");5 _ = @import("big/int.zig");
6 _ = @import("big/rational.zig");
5}7}
std/math/big/rational.zig created+898
...@@ -0,0 +1,898 @@
1const std = @import("../../std.zig");
2const builtin = @import("builtin");
3const debug = std.debug;
4const math = std.math;
5const mem = std.mem;
6const Allocator = mem.Allocator;
7const ArrayList = std.ArrayList;
8
9const TypeId = builtin.TypeId;
10
11const bn = @import("int.zig");
12const Limb = bn.Limb;
13const DoubleLimb = bn.DoubleLimb;
14const Int = bn.Int;
15
16pub const Rational = struct {
17 // sign of Rational is a.positive, b.positive is ignored
18 p: Int,
19 q: Int,
20
21 pub fn init(a: *Allocator) !Rational {
22 return Rational{
23 .p = try Int.init(a),
24 .q = try Int.initSet(a, 1),
25 };
26 }
27
28 pub fn deinit(self: *Rational) void {
29 self.p.deinit();
30 self.q.deinit();
31 }
32
33 pub fn setInt(self: *Rational, a: var) !void {
34 try self.p.set(a);
35 try self.q.set(1);
36 }
37
38 // TODO: Accept a/b fractions and exponent form
39 pub fn setFloatString(self: *Rational, str: []const u8) !void {
40 if (str.len == 0) {
41 return error.InvalidFloatString;
42 }
43
44 const State = enum {
45 Integer,
46 Fractional,
47 };
48
49 var state = State.Integer;
50 var point: ?usize = null;
51
52 var start: usize = 0;
53 if (str[0] == '-') {
54 start += 1;
55 }
56
57 for (str) |c, i| {
58 switch (state) {
59 State.Integer => {
60 switch (c) {
61 '.' => {
62 state = State.Fractional;
63 point = i;
64 },
65 '0'...'9' => {
66 // okay
67 },
68 else => {
69 return error.InvalidFloatString;
70 },
71 }
72 },
73 State.Fractional => {
74 switch (c) {
75 '0'...'9' => {
76 // okay
77 },
78 else => {
79 return error.InvalidFloatString;
80 },
81 }
82 },
83 }
84 }
85
86 // TODO: batch the multiplies by 10
87 if (point) |i| {
88 try self.p.setString(10, str[0..i]);
89
90 const base = Int.initFixed(([]Limb{10})[0..]);
91
92 var j: usize = start;
93 while (j < str.len - i - 1) : (j += 1) {
94 try self.p.mul(self.p, base);
95 }
96
97 try self.q.setString(10, str[i + 1 ..]);
98 try self.p.add(self.p, self.q);
99
100 try self.q.set(1);
101 var k: usize = i + 1;
102 while (k < str.len) : (k += 1) {
103 try self.q.mul(self.q, base);
104 }
105
106 try self.reduce();
107 } else {
108 try self.p.setString(10, str[0..]);
109 try self.q.set(1);
110 }
111 }
112
113 // Translated from golang.go/src/math/big/rat.go.
114 pub fn setFloat(self: *Rational, comptime T: type, f: T) !void {
115 debug.assert(@typeId(T) == builtin.TypeId.Float);
116
117 const UnsignedIntType = @IntType(false, T.bit_count);
118 const f_bits = @bitCast(UnsignedIntType, f);
119
120 const exponent_bits = math.floatExponentBits(T);
121 const exponent_bias = (1 << (exponent_bits - 1)) - 1;
122 const mantissa_bits = math.floatMantissaBits(T);
123
124 const exponent_mask = (1 << exponent_bits) - 1;
125 const mantissa_mask = (1 << mantissa_bits) - 1;
126
127 var exponent = @intCast(i16, (f_bits >> mantissa_bits) & exponent_mask);
128 var mantissa = f_bits & mantissa_mask;
129
130 switch (exponent) {
131 exponent_mask => {
132 return error.NonFiniteFloat;
133 },
134 0 => {
135 // denormal
136 exponent -= exponent_bias - 1;
137 },
138 else => {
139 // normal
140 mantissa |= 1 << mantissa_bits;
141 exponent -= exponent_bias;
142 },
143 }
144
145 var shift: i16 = mantissa_bits - exponent;
146
147 // factor out powers of two early from rational
148 while (mantissa & 1 == 0 and shift > 0) {
149 mantissa >>= 1;
150 shift -= 1;
151 }
152
153 try self.p.set(mantissa);
154 self.p.positive = f >= 0;
155
156 try self.q.set(1);
157 if (shift >= 0) {
158 try self.q.shiftLeft(self.q, @intCast(usize, shift));
159 } else {
160 try self.p.shiftLeft(self.p, @intCast(usize, -shift));
161 }
162
163 try self.reduce();
164 }
165
166 // Translated from golang.go/src/math/big/rat.go.
167 pub fn toFloat(self: Rational, comptime T: type) !T {
168 debug.assert(@typeId(T) == builtin.TypeId.Float);
169
170 const fsize = T.bit_count;
171 const BitReprType = @IntType(false, T.bit_count);
172
173 const msize = math.floatMantissaBits(T);
174 const msize1 = msize + 1;
175 const msize2 = msize1 + 1;
176
177 const esize = math.floatExponentBits(T);
178 const ebias = (1 << (esize - 1)) - 1;
179 const emin = 1 - ebias;
180 const emax = ebias;
181
182 if (self.p.eqZero()) {
183 return 0;
184 }
185
186 // 1. left-shift a or sub so that a/b is in [1 << msize1, 1 << (msize2 + 1)]
187 var exp = @intCast(isize, self.p.bitCountTwosComp()) - @intCast(isize, self.q.bitCountTwosComp());
188
189 var a2 = try self.p.clone();
190 defer a2.deinit();
191
192 var b2 = try self.q.clone();
193 defer b2.deinit();
194
195 const shift = msize2 - exp;
196 if (shift >= 0) {
197 try a2.shiftLeft(a2, @intCast(usize, shift));
198 } else {
199 try b2.shiftLeft(b2, @intCast(usize, -shift));
200 }
201
202 // 2. compute quotient and remainder
203 var q = try Int.init(self.p.allocator.?);
204 defer q.deinit();
205
206 // unused
207 var r = try Int.init(self.p.allocator.?);
208 defer r.deinit();
209
210 try Int.divTrunc(&q, &r, a2, b2);
211
212 var mantissa = extractLowBits(q, BitReprType);
213 var have_rem = r.len > 0;
214
215 // 3. q didn't fit in msize2 bits, redo division b2 << 1
216 if (mantissa >> msize2 == 1) {
217 if (mantissa & 1 == 1) {
218 have_rem = true;
219 }
220 mantissa >>= 1;
221 exp += 1;
222 }
223 if (mantissa >> msize1 != 1) {
224 @panic("unexpected bits in result");
225 }
226
227 // 4. Rounding
228 if (emin - msize <= exp and exp <= emin) {
229 // denormal
230 const shift1 = @intCast(math.Log2Int(BitReprType), emin - (exp - 1));
231 const lost_bits = mantissa & ((@intCast(BitReprType, 1) << shift1) - 1);
232 have_rem = have_rem or lost_bits != 0;
233 mantissa >>= shift1;
234 exp = 2 - ebias;
235 }
236
237 // round q using round-half-to-even
238 var exact = !have_rem;
239 if (mantissa & 1 != 0) {
240 exact = false;
241 if (have_rem or (mantissa & 2 != 0)) {
242 mantissa += 1;
243 if (mantissa >= 1 << msize2) {
244 // 11...1 => 100...0
245 mantissa >>= 1;
246 exp += 1;
247 }
248 }
249 }
250 mantissa >>= 1;
251
252 const f = math.scalbn(@intToFloat(T, mantissa), @intCast(i32, exp - msize1));
253 if (math.isInf(f)) {
254 exact = false;
255 }
256
257 return if (self.p.positive) f else -f;
258 }
259
260 pub fn setRatio(self: *Rational, p: var, q: var) !void {
261 try self.p.set(p);
262 try self.q.set(q);
263
264 self.p.positive = (@boolToInt(self.p.positive) ^ @boolToInt(self.q.positive)) == 0;
265 self.q.positive = true;
266 try self.reduce();
267
268 if (self.q.eqZero()) {
269 @panic("cannot set rational with denominator = 0");
270 }
271 }
272
273 pub fn copyInt(self: *Rational, a: Int) !void {
274 try self.p.copy(a);
275 try self.q.set(1);
276 }
277
278 pub fn copyRatio(self: *Rational, a: Int, b: Int) !void {
279 try self.p.copy(a);
280 try self.q.copy(b);
281
282 self.p.positive = (@boolToInt(self.p.positive) ^ @boolToInt(self.q.positive)) == 0;
283 self.q.positive = true;
284 try self.reduce();
285 }
286
287 pub fn abs(r: *Rational) void {
288 r.p.abs();
289 }
290
291 pub fn negate(r: *Rational) void {
292 r.p.negate();
293 }
294
295 pub fn swap(r: *Rational, other: *Rational) void {
296 r.p.swap(&other.p);
297 r.q.swap(&other.q);
298 }
299
300 pub fn cmp(a: Rational, b: Rational) !i8 {
301 return cmpInternal(a, b, true);
302 }
303
304 pub fn cmpAbs(a: Rational, b: Rational) !i8 {
305 return cmpInternal(a, b, false);
306 }
307
308 // p/q > x/y iff p*y > x*q
309 fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !i8 {
310 // TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid
311 // the memory allocations here?
312 var q = try Int.init(a.p.allocator.?);
313 defer q.deinit();
314
315 var p = try Int.init(b.p.allocator.?);
316 defer p.deinit();
317
318 try q.mul(a.p, b.q);
319 try p.mul(b.p, a.q);
320
321 return if (is_abs) q.cmpAbs(p) else q.cmp(p);
322 }
323
324 // r/q = ap/aq + bp/bq = (ap*bq + bp*aq) / (aq*bq)
325 //
326 // For best performance, rma should not alias a or b.
327 pub fn add(rma: *Rational, a: Rational, b: Rational) !void {
328 var r = rma;
329 var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr;
330
331 var sr: Rational = undefined;
332 if (aliased) {
333 sr = try Rational.init(rma.p.allocator.?);
334 r = &sr;
335 aliased = true;
336 }
337 defer if (aliased) {
338 rma.swap(r);
339 r.deinit();
340 };
341
342 try r.p.mul(a.p, b.q);
343 try r.q.mul(b.p, a.q);
344 try r.p.add(r.p, r.q);
345
346 try r.q.mul(a.q, b.q);
347 try r.reduce();
348 }
349
350 // r/q = ap/aq - bp/bq = (ap*bq - bp*aq) / (aq*bq)
351 //
352 // For best performance, rma should not alias a or b.
353 pub fn sub(rma: *Rational, a: Rational, b: Rational) !void {
354 var r = rma;
355 var aliased = rma.p.limbs.ptr == a.p.limbs.ptr or rma.p.limbs.ptr == b.p.limbs.ptr;
356
357 var sr: Rational = undefined;
358 if (aliased) {
359 sr = try Rational.init(rma.p.allocator.?);
360 r = &sr;
361 aliased = true;
362 }
363 defer if (aliased) {
364 rma.swap(r);
365 r.deinit();
366 };
367
368 try r.p.mul(a.p, b.q);
369 try r.q.mul(b.p, a.q);
370 try r.p.sub(r.p, r.q);
371
372 try r.q.mul(a.q, b.q);
373 try r.reduce();
374 }
375
376 // r/q = ap/aq * bp/bq = ap*bp / aq*bq
377 pub fn mul(r: *Rational, a: Rational, b: Rational) !void {
378 try r.p.mul(a.p, b.p);
379 try r.q.mul(a.q, b.q);
380 try r.reduce();
381 }
382
383 // r/q = (ap/aq) / (bp/bq) = ap*bq / bp*aq
384 pub fn div(r: *Rational, a: Rational, b: Rational) !void {
385 if (b.p.eqZero()) {
386 @panic("division by zero");
387 }
388
389 try r.p.mul(a.p, b.q);
390 try r.q.mul(b.p, a.q);
391 try r.reduce();
392 }
393
394 // r/q = q/r
395 pub fn invert(r: *Rational) void {
396 Int.swap(&r.p, &r.q);
397 }
398
399 // reduce r/q such that gcd(r, q) = 1
400 fn reduce(r: *Rational) !void {
401 var a = try Int.init(r.p.allocator.?);
402 defer a.deinit();
403
404 const sign = r.p.positive;
405
406 r.p.abs();
407 try gcd(&a, r.p, r.q);
408 r.p.positive = sign;
409
410 const one = Int.initFixed(([]Limb{1})[0..]);
411 if (a.cmp(one) != 0) {
412 var unused = try Int.init(r.p.allocator.?);
413 defer unused.deinit();
414
415 // TODO: divexact would be useful here
416 // TODO: don't copy r.q for div
417 try Int.divTrunc(&r.p, &unused, r.p, a);
418 try Int.divTrunc(&r.q, &unused, r.q, a);
419 }
420 }
421};
422
423var al = debug.global_allocator;
424
425const SignedDoubleLimb = @IntType(true, DoubleLimb.bit_count);
426
427fn gcd(rma: *Int, x: Int, y: Int) !void {
428 var r = rma;
429 var aliased = rma.limbs.ptr == x.limbs.ptr or rma.limbs.ptr == y.limbs.ptr;
430
431 var sr: Int = undefined;
432 if (aliased) {
433 sr = try Int.initCapacity(rma.allocator.?, math.max(x.len, y.len));
434 r = &sr;
435 aliased = true;
436 }
437 defer if (aliased) {
438 rma.swap(r);
439 r.deinit();
440 };
441
442 if (x.cmp(y) > 0) {
443 try gcdLehmer(r, x, y);
444 } else {
445 try gcdLehmer(r, y, x);
446 }
447}
448
449// Storage must live for the lifetime of the returned value
450fn FixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Int {
451 std.debug.assert(storage.len >= 2);
452
453 var A_is_positive = A >= 0;
454 const Au = @intCast(DoubleLimb, if (A < 0) -A else A);
455 storage[0] = @truncate(Limb, Au);
456 storage[1] = @truncate(Limb, Au >> Limb.bit_count);
457 var Ap = Int.initFixed(storage[0..2]);
458 Ap.positive = A_is_positive;
459 return Ap;
460}
461
462// Handbook of Applied Cryptography, 14.57
463//
464// r = gcd(x, y) where x, y > 0
465fn gcdLehmer(r: *Int, xa: Int, ya: Int) !void {
466 debug.assert(xa.positive and ya.positive);
467 debug.assert(xa.cmp(ya) >= 0);
468
469 var x = try xa.clone();
470 defer x.deinit();
471
472 var y = try ya.clone();
473 defer y.deinit();
474
475 var T = try Int.init(r.allocator.?);
476 defer T.deinit();
477
478 while (y.len > 1) {
479 debug.assert(x.len >= y.len);
480
481 // chop the leading zeros of the limbs and normalize
482 const offset = @clz(x.limbs[x.len - 1]);
483
484 var xh: SignedDoubleLimb = math.shl(Limb, x.limbs[x.len - 1], offset) |
485 math.shr(Limb, x.limbs[x.len - 2], Limb.bit_count - offset);
486
487 var yh: SignedDoubleLimb = if (y.len == x.len)
488 math.shl(Limb, y.limbs[y.len - 1], offset) | math.shr(Limb, y.limbs[y.len - 2], Limb.bit_count - offset)
489 else if (y.len == x.len - 1)
490 math.shr(Limb, y.limbs[y.len - 2], Limb.bit_count - offset)
491 else
492 0;
493
494 var A: SignedDoubleLimb = 1;
495 var B: SignedDoubleLimb = 0;
496 var C: SignedDoubleLimb = 0;
497 var D: SignedDoubleLimb = 1;
498
499 while (yh + C != 0 and yh + D != 0) {
500 const q = @divFloor(xh + A, yh + C);
501 const qp = @divFloor(xh + B, yh + D);
502 if (q != qp) {
503 break;
504 }
505
506 var t = A - q * C;
507 A = C;
508 C = t;
509 t = B - q * D;
510 B = D;
511 D = t;
512
513 t = xh - q * yh;
514 xh = yh;
515 yh = t;
516 }
517
518 if (B == 0) {
519 // T = x % y, r is unused
520 try Int.divTrunc(r, &T, x, y);
521 debug.assert(T.positive);
522
523 x.swap(&y);
524 y.swap(&T);
525 } else {
526 var storage: [8]Limb = undefined;
527 const Ap = FixedIntFromSignedDoubleLimb(A, storage[0..2]);
528 const Bp = FixedIntFromSignedDoubleLimb(B, storage[2..4]);
529 const Cp = FixedIntFromSignedDoubleLimb(C, storage[4..6]);
530 const Dp = FixedIntFromSignedDoubleLimb(D, storage[6..8]);
531
532 // T = Ax + By
533 try r.mul(x, Ap);
534 try T.mul(y, Bp);
535 try T.add(r.*, T);
536
537 // u = Cx + Dy, r as u
538 try x.mul(x, Cp);
539 try r.mul(y, Dp);
540 try r.add(x, r.*);
541
542 x.swap(&T);
543 y.swap(r);
544 }
545 }
546
547 // euclidean algorithm
548 debug.assert(x.cmp(y) >= 0);
549
550 while (!y.eqZero()) {
551 try Int.divTrunc(&T, r, x, y);
552 x.swap(&y);
553 y.swap(r);
554 }
555
556 r.swap(&x);
557}
558
559test "big.rational gcd non-one small" {
560 var a = try Int.initSet(al, 17);
561 var b = try Int.initSet(al, 97);
562 var r = try Int.init(al);
563
564 try gcd(&r, a, b);
565
566 debug.assert((try r.to(u32)) == 1);
567}
568
569test "big.rational gcd non-one small" {
570 var a = try Int.initSet(al, 4864);
571 var b = try Int.initSet(al, 3458);
572 var r = try Int.init(al);
573
574 try gcd(&r, a, b);
575
576 debug.assert((try r.to(u32)) == 38);
577}
578
579test "big.rational gcd non-one large" {
580 var a = try Int.initSet(al, 0xffffffffffffffff);
581 var b = try Int.initSet(al, 0xffffffffffffffff7777);
582 var r = try Int.init(al);
583
584 try gcd(&r, a, b);
585
586 debug.assert((try r.to(u32)) == 4369);
587}
588
589test "big.rational gcd large multi-limb result" {
590 var a = try Int.initSet(al, 0x12345678123456781234567812345678123456781234567812345678);
591 var b = try Int.initSet(al, 0x12345671234567123456712345671234567123456712345671234567);
592 var r = try Int.init(al);
593
594 try gcd(&r, a, b);
595
596 debug.assert((try r.to(u256)) == 0xf000000ff00000fff0000ffff000fffff00ffffff1);
597}
598
599fn extractLowBits(a: Int, comptime T: type) T {
600 debug.assert(@typeId(T) == builtin.TypeId.Int);
601
602 if (T.bit_count <= Limb.bit_count) {
603 return @truncate(T, a.limbs[0]);
604 } else {
605 var r: T = 0;
606 comptime var i: usize = 0;
607
608 // Remainder is always 0 since if T.bit_count >= Limb.bit_count -> Limb | T and both
609 // are powers of two.
610 inline while (i < T.bit_count / Limb.bit_count) : (i += 1) {
611 r |= math.shl(T, a.limbs[i], i * Limb.bit_count);
612 }
613
614 return r;
615 }
616}
617
618test "big.rational extractLowBits" {
619 var a = try Int.initSet(al, 0x11112222333344441234567887654321);
620
621 const a1 = extractLowBits(a, u8);
622 debug.assert(a1 == 0x21);
623
624 const a2 = extractLowBits(a, u16);
625 debug.assert(a2 == 0x4321);
626
627 const a3 = extractLowBits(a, u32);
628 debug.assert(a3 == 0x87654321);
629
630 const a4 = extractLowBits(a, u64);
631 debug.assert(a4 == 0x1234567887654321);
632
633 const a5 = extractLowBits(a, u128);
634 debug.assert(a5 == 0x11112222333344441234567887654321);
635}
636
637test "big.rational set" {
638 var a = try Rational.init(al);
639
640 try a.setInt(5);
641 debug.assert((try a.p.to(u32)) == 5);
642 debug.assert((try a.q.to(u32)) == 1);
643
644 try a.setRatio(7, 3);
645 debug.assert((try a.p.to(u32)) == 7);
646 debug.assert((try a.q.to(u32)) == 3);
647
648 try a.setRatio(9, 3);
649 debug.assert((try a.p.to(i32)) == 3);
650 debug.assert((try a.q.to(i32)) == 1);
651
652 try a.setRatio(-9, 3);
653 debug.assert((try a.p.to(i32)) == -3);
654 debug.assert((try a.q.to(i32)) == 1);
655
656 try a.setRatio(9, -3);
657 debug.assert((try a.p.to(i32)) == -3);
658 debug.assert((try a.q.to(i32)) == 1);
659
660 try a.setRatio(-9, -3);
661 debug.assert((try a.p.to(i32)) == 3);
662 debug.assert((try a.q.to(i32)) == 1);
663}
664
665test "big.rational setFloat" {
666 var a = try Rational.init(al);
667
668 try a.setFloat(f64, 2.5);
669 debug.assert((try a.p.to(i32)) == 5);
670 debug.assert((try a.q.to(i32)) == 2);
671
672 try a.setFloat(f32, -2.5);
673 debug.assert((try a.p.to(i32)) == -5);
674 debug.assert((try a.q.to(i32)) == 2);
675
676 try a.setFloat(f32, 3.141593);
677
678 // = 3.14159297943115234375
679 debug.assert((try a.p.to(u32)) == 3294199);
680 debug.assert((try a.q.to(u32)) == 1048576);
681
682 try a.setFloat(f64, 72.141593120712409172417410926841290461290467124);
683
684 // = 72.1415931207124145885245525278151035308837890625
685 debug.assert((try a.p.to(u128)) == 5076513310880537);
686 debug.assert((try a.q.to(u128)) == 70368744177664);
687}
688
689test "big.rational setFloatString" {
690 var a = try Rational.init(al);
691
692 try a.setFloatString("72.14159312071241458852455252781510353");
693
694 // = 72.1415931207124145885245525278151035308837890625
695 debug.assert((try a.p.to(u128)) == 7214159312071241458852455252781510353);
696 debug.assert((try a.q.to(u128)) == 100000000000000000000000000000000000);
697}
698
699test "big.rational toFloat" {
700 var a = try Rational.init(al);
701
702 // = 3.14159297943115234375
703 try a.setRatio(3294199, 1048576);
704 debug.assert((try a.toFloat(f64)) == 3.14159297943115234375);
705
706 // = 72.1415931207124145885245525278151035308837890625
707 try a.setRatio(5076513310880537, 70368744177664);
708 debug.assert((try a.toFloat(f64)) == 72.141593120712409172417410926841290461290467124);
709}
710
711test "big.rational set/to Float round-trip" {
712 // toFloat allocates memory in a loop so we need to free it
713 var buf: [512 * 1024]u8 = undefined;
714 var fixed = std.heap.FixedBufferAllocator.init(buf[0..]);
715
716 var a = try Rational.init(&fixed.allocator);
717
718 var prng = std.rand.DefaultPrng.init(0x5EED);
719 var i: usize = 0;
720 while (i < 512) : (i += 1) {
721 const r = prng.random.float(f64);
722 try a.setFloat(f64, r);
723 debug.assert((try a.toFloat(f64)) == r);
724 }
725}
726
727test "big.rational copy" {
728 var a = try Rational.init(al);
729
730 const b = try Int.initSet(al, 5);
731
732 try a.copyInt(b);
733 debug.assert((try a.p.to(u32)) == 5);
734 debug.assert((try a.q.to(u32)) == 1);
735
736 const c = try Int.initSet(al, 7);
737 const d = try Int.initSet(al, 3);
738
739 try a.copyRatio(c, d);
740 debug.assert((try a.p.to(u32)) == 7);
741 debug.assert((try a.q.to(u32)) == 3);
742
743 const e = try Int.initSet(al, 9);
744 const f = try Int.initSet(al, 3);
745
746 try a.copyRatio(e, f);
747 debug.assert((try a.p.to(u32)) == 3);
748 debug.assert((try a.q.to(u32)) == 1);
749}
750
751test "big.rational negate" {
752 var a = try Rational.init(al);
753
754 try a.setInt(-50);
755 debug.assert((try a.p.to(i32)) == -50);
756 debug.assert((try a.q.to(i32)) == 1);
757
758 a.negate();
759 debug.assert((try a.p.to(i32)) == 50);
760 debug.assert((try a.q.to(i32)) == 1);
761
762 a.negate();
763 debug.assert((try a.p.to(i32)) == -50);
764 debug.assert((try a.q.to(i32)) == 1);
765}
766
767test "big.rational abs" {
768 var a = try Rational.init(al);
769
770 try a.setInt(-50);
771 debug.assert((try a.p.to(i32)) == -50);
772 debug.assert((try a.q.to(i32)) == 1);
773
774 a.abs();
775 debug.assert((try a.p.to(i32)) == 50);
776 debug.assert((try a.q.to(i32)) == 1);
777
778 a.abs();
779 debug.assert((try a.p.to(i32)) == 50);
780 debug.assert((try a.q.to(i32)) == 1);
781}
782
783test "big.rational swap" {
784 var a = try Rational.init(al);
785 var b = try Rational.init(al);
786
787 try a.setRatio(50, 23);
788 try b.setRatio(17, 3);
789
790 debug.assert((try a.p.to(u32)) == 50);
791 debug.assert((try a.q.to(u32)) == 23);
792
793 debug.assert((try b.p.to(u32)) == 17);
794 debug.assert((try b.q.to(u32)) == 3);
795
796 a.swap(&b);
797
798 debug.assert((try a.p.to(u32)) == 17);
799 debug.assert((try a.q.to(u32)) == 3);
800
801 debug.assert((try b.p.to(u32)) == 50);
802 debug.assert((try b.q.to(u32)) == 23);
803}
804
805test "big.rational cmp" {
806 var a = try Rational.init(al);
807 var b = try Rational.init(al);
808
809 try a.setRatio(500, 231);
810 try b.setRatio(18903, 8584);
811 debug.assert((try a.cmp(b)) < 0);
812
813 try a.setRatio(890, 10);
814 try b.setRatio(89, 1);
815 debug.assert((try a.cmp(b)) == 0);
816}
817
818test "big.rational add single-limb" {
819 var a = try Rational.init(al);
820 var b = try Rational.init(al);
821
822 try a.setRatio(500, 231);
823 try b.setRatio(18903, 8584);
824 debug.assert((try a.cmp(b)) < 0);
825
826 try a.setRatio(890, 10);
827 try b.setRatio(89, 1);
828 debug.assert((try a.cmp(b)) == 0);
829}
830
831test "big.rational add" {
832 var a = try Rational.init(al);
833 var b = try Rational.init(al);
834 var r = try Rational.init(al);
835
836 try a.setRatio(78923, 23341);
837 try b.setRatio(123097, 12441414);
838 try a.add(a, b);
839
840 try r.setRatio(984786924199, 290395044174);
841 debug.assert((try a.cmp(r)) == 0);
842}
843
844test "big.rational sub" {
845 var a = try Rational.init(al);
846 var b = try Rational.init(al);
847 var r = try Rational.init(al);
848
849 try a.setRatio(78923, 23341);
850 try b.setRatio(123097, 12441414);
851 try a.sub(a, b);
852
853 try r.setRatio(979040510045, 290395044174);
854 debug.assert((try a.cmp(r)) == 0);
855}
856
857test "big.rational mul" {
858 var a = try Rational.init(al);
859 var b = try Rational.init(al);
860 var r = try Rational.init(al);
861
862 try a.setRatio(78923, 23341);
863 try b.setRatio(123097, 12441414);
864 try a.mul(a, b);
865
866 try r.setRatio(571481443, 17082061422);
867 debug.assert((try a.cmp(r)) == 0);
868}
869
870test "big.rational div" {
871 var a = try Rational.init(al);
872 var b = try Rational.init(al);
873 var r = try Rational.init(al);
874
875 try a.setRatio(78923, 23341);
876 try b.setRatio(123097, 12441414);
877 try a.div(a, b);
878
879 try r.setRatio(75531824394, 221015929);
880 debug.assert((try a.cmp(r)) == 0);
881}
882
883test "big.rational div" {
884 var a = try Rational.init(al);
885 var r = try Rational.init(al);
886
887 try a.setRatio(78923, 23341);
888 a.invert();
889
890 try r.setRatio(23341, 78923);
891 debug.assert((try a.cmp(r)) == 0);
892
893 try a.setRatio(-78923, 23341);
894 a.invert();
895
896 try r.setRatio(-23341, 78923);
897 debug.assert((try a.cmp(r)) == 0);
898}