| ... | ... | @@ -22,7 +22,7 @@ comptime { |
| 22 | 22 | } |
| 23 | 23 | |
| 24 | 24 | pub const Int = struct { |
| 25 | | allocator: *Allocator, |
| 25 | allocator: ?*Allocator, |
| 26 | 26 | positive: bool, |
| 27 | 27 | // - little-endian ordered |
| 28 | 28 | // - len >= 1 always |
| ... | ... | @@ -55,16 +55,40 @@ pub const Int = struct { |
| 55 | 55 | }; |
| 56 | 56 | } |
| 57 | 57 | |
| 58 | // Initialize an Int directly from a fixed set of limb values. This is considered read-only |
| 59 | // and cannot be used as a receiver argument to any functions. If this tries to allocate |
| 60 | // at any point a panic will occur due to the null allocator. |
| 61 | pub fn initFixed(limbs: []const Limb) Int { |
| 62 | var self = Int{ |
| 63 | .allocator = null, |
| 64 | .positive = true, |
| 65 | // Cast away the const, invalid use to pass as a pointer argument. |
| 66 | .limbs = @intToPtr([*]Limb, @ptrToInt(limbs.ptr))[0..limbs.len], |
| 67 | .len = limbs.len, |
| 68 | }; |
| 69 | |
| 70 | self.normN(limbs.len); |
| 71 | return self; |
| 72 | } |
| 73 | |
| 58 | 74 | pub fn ensureCapacity(self: *Int, capacity: usize) !void { |
| 75 | self.assertWritable(); |
| 59 | 76 | if (capacity <= self.limbs.len) { |
| 60 | 77 | return; |
| 61 | 78 | } |
| 62 | 79 | |
| 63 | | self.limbs = try self.allocator.realloc(self.limbs, capacity); |
| 80 | self.limbs = try self.allocator.?.realloc(self.limbs, capacity); |
| 81 | } |
| 82 | |
| 83 | fn assertWritable(self: Int) void { |
| 84 | if (self.allocator == null) { |
| 85 | @panic("provided Int value is read-only but must be writable"); |
| 86 | } |
| 64 | 87 | } |
| 65 | 88 | |
| 66 | 89 | pub fn deinit(self: *Int) void { |
| 67 | | self.allocator.free(self.limbs); |
| 90 | self.assertWritable(); |
| 91 | self.allocator.?.free(self.limbs); |
| 68 | 92 | self.* = undefined; |
| 69 | 93 | } |
| 70 | 94 | |
| ... | ... | @@ -73,7 +97,7 @@ pub const Int = struct { |
| 73 | 97 | .allocator = other.allocator, |
| 74 | 98 | .positive = other.positive, |
| 75 | 99 | .limbs = block: { |
| 76 | | var limbs = try other.allocator.alloc(Limb, other.len); |
| 100 | var limbs = try other.allocator.?.alloc(Limb, other.len); |
| 77 | 101 | mem.copy(Limb, limbs[0..], other.limbs[0..other.len]); |
| 78 | 102 | break :block limbs; |
| 79 | 103 | }, |
| ... | ... | @@ -82,7 +106,8 @@ pub const Int = struct { |
| 82 | 106 | } |
| 83 | 107 | |
| 84 | 108 | pub fn copy(self: *Int, other: Int) !void { |
| 85 | | if (self == &other) { |
| 109 | self.assertWritable(); |
| 110 | if (self.limbs.ptr == other.limbs.ptr) { |
| 86 | 111 | return; |
| 87 | 112 | } |
| 88 | 113 | |
| ... | ... | @@ -93,6 +118,7 @@ pub const Int = struct { |
| 93 | 118 | } |
| 94 | 119 | |
| 95 | 120 | pub fn swap(self: *Int, other: *Int) void { |
| 121 | self.assertWritable(); |
| 96 | 122 | mem.swap(Int, self, other); |
| 97 | 123 | } |
| 98 | 124 | |
| ... | ... | @@ -103,20 +129,20 @@ pub const Int = struct { |
| 103 | 129 | debug.warn("\n"); |
| 104 | 130 | } |
| 105 | 131 | |
| 106 | | pub fn negate(r: *Int) void { |
| 107 | | r.positive = !r.positive; |
| 132 | pub fn negate(self: *Int) void { |
| 133 | self.positive = !self.positive; |
| 108 | 134 | } |
| 109 | 135 | |
| 110 | | pub fn abs(r: *Int) void { |
| 111 | | r.positive = true; |
| 136 | pub fn abs(self: *Int) void { |
| 137 | self.positive = true; |
| 112 | 138 | } |
| 113 | 139 | |
| 114 | | pub fn isOdd(r: Int) bool { |
| 115 | | return r.limbs[0] & 1 != 0; |
| 140 | pub fn isOdd(self: Int) bool { |
| 141 | return self.limbs[0] & 1 != 0; |
| 116 | 142 | } |
| 117 | 143 | |
| 118 | | pub fn isEven(r: Int) bool { |
| 119 | | return !r.isOdd(); |
| 144 | pub fn isEven(self: Int) bool { |
| 145 | return !self.isOdd(); |
| 120 | 146 | } |
| 121 | 147 | |
| 122 | 148 | // Returns the number of bits required to represent the absolute value of self. |
| ... | ... | @@ -179,6 +205,7 @@ pub const Int = struct { |
| 179 | 205 | } |
| 180 | 206 | |
| 181 | 207 | pub fn set(self: *Int, value: var) Allocator.Error!void { |
| 208 | self.assertWritable(); |
| 182 | 209 | const T = @typeOf(value); |
| 183 | 210 | |
| 184 | 211 | switch (@typeInfo(T)) { |
| ... | ... | @@ -304,6 +331,7 @@ pub const Int = struct { |
| 304 | 331 | } |
| 305 | 332 | |
| 306 | 333 | pub fn setString(self: *Int, base: u8, value: []const u8) !void { |
| 334 | self.assertWritable(); |
| 307 | 335 | if (base < 2 or base > 16) { |
| 308 | 336 | return error.InvalidBase; |
| 309 | 337 | } |
| ... | ... | @@ -315,23 +343,16 @@ pub const Int = struct { |
| 315 | 343 | i += 1; |
| 316 | 344 | } |
| 317 | 345 | |
| 318 | | // TODO values less than limb size should guarantee non allocating |
| 319 | | var base_buffer: [512]u8 = undefined; |
| 320 | | const base_al = &std.heap.FixedBufferAllocator.init(base_buffer[0..]).allocator; |
| 321 | | const base_ap = try Int.initSet(base_al, base); |
| 322 | | |
| 323 | | var d_buffer: [512]u8 = undefined; |
| 324 | | var d_fba = std.heap.FixedBufferAllocator.init(d_buffer[0..]); |
| 325 | | const d_al = &d_fba.allocator; |
| 326 | | |
| 346 | const ap_base = Int.initFixed(([]Limb{base})[0..]); |
| 327 | 347 | try self.set(0); |
| 348 | |
| 328 | 349 | for (value[i..]) |ch| { |
| 329 | 350 | const d = try charToDigit(ch, base); |
| 330 | | d_fba.end_index = 0; |
| 331 | | const d_ap = try Int.initSet(d_al, d); |
| 332 | 351 | |
| 333 | | try self.mul(self.*, base_ap); |
| 334 | | try self.add(self.*, d_ap); |
| 352 | const ap_d = Int.initFixed(([]Limb{d})[0..]); |
| 353 | |
| 354 | try self.mul(self.*, ap_base); |
| 355 | try self.add(self.*, ap_d); |
| 335 | 356 | } |
| 336 | 357 | self.positive = positive; |
| 337 | 358 | } |
| ... | ... | @@ -520,8 +541,19 @@ pub const Int = struct { |
| 520 | 541 | r.len = if (j != 0) j else 1; |
| 521 | 542 | } |
| 522 | 543 | |
| 544 | // Cannot be used as a result argument to any function. |
| 545 | fn readOnlyPositive(a: Int) Int { |
| 546 | return Int{ |
| 547 | .allocator = null, |
| 548 | .positive = true, |
| 549 | .limbs = a.limbs, |
| 550 | .len = a.len, |
| 551 | }; |
| 552 | } |
| 553 | |
| 523 | 554 | // r = a + b |
| 524 | 555 | pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void { |
| 556 | r.assertWritable(); |
| 525 | 557 | if (a.eqZero()) { |
| 526 | 558 | try r.copy(b); |
| 527 | 559 | return; |
| ... | ... | @@ -533,22 +565,10 @@ pub const Int = struct { |
| 533 | 565 | if (a.positive != b.positive) { |
| 534 | 566 | if (a.positive) { |
| 535 | 567 | // (a) + (-b) => a - b |
| 536 | | const bp = Int{ |
| 537 | | .allocator = undefined, |
| 538 | | .positive = true, |
| 539 | | .limbs = b.limbs, |
| 540 | | .len = b.len, |
| 541 | | }; |
| 542 | | try r.sub(a, bp); |
| 568 | try r.sub(a, readOnlyPositive(b)); |
| 543 | 569 | } else { |
| 544 | 570 | // (-a) + (b) => b - a |
| 545 | | const ap = Int{ |
| 546 | | .allocator = undefined, |
| 547 | | .positive = true, |
| 548 | | .limbs = a.limbs, |
| 549 | | .len = a.len, |
| 550 | | }; |
| 551 | | try r.sub(b, ap); |
| 571 | try r.sub(b, readOnlyPositive(a)); |
| 552 | 572 | } |
| 553 | 573 | } else { |
| 554 | 574 | if (a.len >= b.len) { |
| ... | ... | @@ -591,25 +611,14 @@ pub const Int = struct { |
| 591 | 611 | |
| 592 | 612 | // r = a - b |
| 593 | 613 | pub fn sub(r: *Int, a: Int, b: Int) !void { |
| 614 | r.assertWritable(); |
| 594 | 615 | if (a.positive != b.positive) { |
| 595 | 616 | if (a.positive) { |
| 596 | 617 | // (a) - (-b) => a + b |
| 597 | | const bp = Int{ |
| 598 | | .allocator = undefined, |
| 599 | | .positive = true, |
| 600 | | .limbs = b.limbs, |
| 601 | | .len = b.len, |
| 602 | | }; |
| 603 | | try r.add(a, bp); |
| 618 | try r.add(a, readOnlyPositive(b)); |
| 604 | 619 | } else { |
| 605 | 620 | // (-a) - (b) => -(a + b) |
| 606 | | const ap = Int{ |
| 607 | | .allocator = undefined, |
| 608 | | .positive = true, |
| 609 | | .limbs = a.limbs, |
| 610 | | .len = a.len, |
| 611 | | }; |
| 612 | | try r.add(ap, b); |
| 621 | try r.add(readOnlyPositive(a), b); |
| 613 | 622 | r.positive = false; |
| 614 | 623 | } |
| 615 | 624 | } else { |
| ... | ... | @@ -671,12 +680,14 @@ pub const Int = struct { |
| 671 | 680 | // |
| 672 | 681 | // For greatest efficiency, ensure rma does not alias a or b. |
| 673 | 682 | pub fn mul(rma: *Int, a: Int, b: Int) !void { |
| 683 | rma.assertWritable(); |
| 684 | |
| 674 | 685 | var r = rma; |
| 675 | 686 | var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr; |
| 676 | 687 | |
| 677 | 688 | var sr: Int = undefined; |
| 678 | 689 | if (aliased) { |
| 679 | | sr = try Int.initCapacity(rma.allocator, a.len + b.len); |
| 690 | sr = try Int.initCapacity(rma.allocator.?, a.len + b.len); |
| 680 | 691 | r = &sr; |
| 681 | 692 | aliased = true; |
| 682 | 693 | } |
| ... | ... | @@ -745,13 +756,9 @@ pub const Int = struct { |
| 745 | 756 | |
| 746 | 757 | // Trunc -> Floor. |
| 747 | 758 | if (!q.positive) { |
| 748 | | // TODO values less than limb size should guarantee non allocating |
| 749 | | var one_buffer: [512]u8 = undefined; |
| 750 | | const one_al = &std.heap.FixedBufferAllocator.init(one_buffer[0..]).allocator; |
| 751 | | const one_ap = try Int.initSet(one_al, 1); |
| 752 | | |
| 753 | | try q.sub(q.*, one_ap); |
| 754 | | try r.add(q.*, one_ap); |
| 759 | const one = Int.initFixed(([]Limb{1})[0..]); |
| 760 | try q.sub(q.*, one); |
| 761 | try r.add(q.*, one); |
| 755 | 762 | } |
| 756 | 763 | r.positive = b.positive; |
| 757 | 764 | } |
| ... | ... | @@ -763,6 +770,9 @@ pub const Int = struct { |
| 763 | 770 | |
| 764 | 771 | // Truncates by default. |
| 765 | 772 | fn div(quo: *Int, rem: *Int, a: Int, b: Int) !void { |
| 773 | quo.assertWritable(); |
| 774 | rem.assertWritable(); |
| 775 | |
| 766 | 776 | if (b.eqZero()) { |
| 767 | 777 | @panic("division by zero"); |
| 768 | 778 | } |
| ... | ... | @@ -800,7 +810,7 @@ pub const Int = struct { |
| 800 | 810 | |
| 801 | 811 | // x may grow one limb during normalization |
| 802 | 812 | try quo.ensureCapacity(a.len + y.len); |
| 803 | | try divN(quo.allocator, quo, rem, &x, &y); |
| 813 | try divN(quo.allocator.?, quo, rem, &x, &y); |
| 804 | 814 | |
| 805 | 815 | quo.positive = a.positive == b.positive; |
| 806 | 816 | } |
| ... | ... | @@ -919,6 +929,8 @@ pub const Int = struct { |
| 919 | 929 | |
| 920 | 930 | // r = a << shift, in other words, r = a * 2^shift |
| 921 | 931 | pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void { |
| 932 | r.assertWritable(); |
| 933 | |
| 922 | 934 | try r.ensureCapacity(a.len + (shift / Limb.bit_count) + 1); |
| 923 | 935 | llshl(r.limbs[0..], a.limbs[0..a.len], shift); |
| 924 | 936 | r.norm1(a.len + (shift / Limb.bit_count) + 1); |
| ... | ... | @@ -950,6 +962,8 @@ pub const Int = struct { |
| 950 | 962 | |
| 951 | 963 | // r = a >> shift |
| 952 | 964 | pub fn shiftRight(r: *Int, a: Int, shift: usize) !void { |
| 965 | r.assertWritable(); |
| 966 | |
| 953 | 967 | if (a.len <= shift / Limb.bit_count) { |
| 954 | 968 | r.len = 1; |
| 955 | 969 | r.limbs[0] = 0; |
| ... | ... | @@ -985,6 +999,8 @@ pub const Int = struct { |
| 985 | 999 | |
| 986 | 1000 | // r = a | b |
| 987 | 1001 | pub fn bitOr(r: *Int, a: Int, b: Int) !void { |
| 1002 | r.assertWritable(); |
| 1003 | |
| 988 | 1004 | if (a.len > b.len) { |
| 989 | 1005 | try r.ensureCapacity(a.len); |
| 990 | 1006 | llor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]); |
| ... | ... | @@ -1012,6 +1028,8 @@ pub const Int = struct { |
| 1012 | 1028 | |
| 1013 | 1029 | // r = a & b |
| 1014 | 1030 | pub fn bitAnd(r: *Int, a: Int, b: Int) !void { |
| 1031 | r.assertWritable(); |
| 1032 | |
| 1015 | 1033 | if (a.len > b.len) { |
| 1016 | 1034 | try r.ensureCapacity(b.len); |
| 1017 | 1035 | lland(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]); |
| ... | ... | @@ -1036,6 +1054,8 @@ pub const Int = struct { |
| 1036 | 1054 | |
| 1037 | 1055 | // r = a ^ b |
| 1038 | 1056 | pub fn bitXor(r: *Int, a: Int, b: Int) !void { |
| 1057 | r.assertWritable(); |
| 1058 | |
| 1039 | 1059 | if (a.len > b.len) { |
| 1040 | 1060 | try r.ensureCapacity(a.len); |
| 1041 | 1061 | llxor(r.limbs[0..], a.limbs[0..a.len], b.limbs[0..b.len]); |