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