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