| author | |
| committer | |
| log | 3386bb896d071eef4ff571fac399e18b2270a382 |
| tree | c3e597506a6f5a41269acdd386fd87bd473cdaa9 |
| parent | 94b0d0e80242563f4ad7ad41e3c0f5193a60b70c |
| parent | ec6ef86219578822fd32bbe2e5eb83b24ddfdca6 |
| signature | Signed by PGP key 4AEE18F83AFDEB23 |
add ZIR compare output test case to test suite27 files changed, 5679 insertions(+), 3480 deletions(-)
build.zig+1-2| ... | ... | @@ -44,7 +44,7 @@ pub fn build(b: *Builder) !void { |
| 44 | 44 | try findAndReadConfigH(b); |
| 45 | 45 | |
| 46 | 46 | var test_stage2 = b.addTest("src-self-hosted/test.zig"); |
| 47 | test_stage2.setBuildMode(builtin.Mode.Debug); | |
| 47 | test_stage2.setBuildMode(.Debug); // note this is only the mode of the test harness | |
| 48 | 48 | test_stage2.addPackagePath("stage2_tests", "test/stage2/test.zig"); |
| 49 | 49 | |
| 50 | 50 | const fmt_build_zig = b.addFmt(&[_][]const u8{"build.zig"}); |
| ... | ... | @@ -68,7 +68,6 @@ pub fn build(b: *Builder) !void { |
| 68 | 68 | var ctx = parseConfigH(b, config_h_text); |
| 69 | 69 | ctx.llvm = try findLLVM(b, ctx.llvm_config_exe); |
| 70 | 70 | |
| 71 | try configureStage2(b, test_stage2, ctx); | |
| 72 | 71 | try configureStage2(b, exe, ctx); |
| 73 | 72 | |
| 74 | 73 | b.default_step.dependOn(&exe.step); |
lib/std/child_process.zig+11-1| ... | ... | @@ -46,6 +46,12 @@ pub const ChildProcess = struct { |
| 46 | 46 | |
| 47 | 47 | /// Set to change the current working directory when spawning the child process. |
| 48 | 48 | cwd: ?[]const u8, |
| 49 | /// Set to change the current working directory when spawning the child process. | |
| 50 | /// This is not yet implemented for Windows. See https://github.com/ziglang/zig/issues/5190 | |
| 51 | /// Once that is done, `cwd` will be deprecated in favor of this field. | |
| 52 | /// The directory handle must be opened with the ability to be passed | |
| 53 | /// to a child process (no `O_CLOEXEC` flag on POSIX). | |
| 54 | cwd_dir: ?fs.Dir = null, | |
| 49 | 55 | |
| 50 | 56 | err_pipe: if (builtin.os.tag == .windows) void else [2]os.fd_t, |
| 51 | 57 | |
| ... | ... | @@ -183,6 +189,7 @@ pub const ChildProcess = struct { |
| 183 | 189 | allocator: *mem.Allocator, |
| 184 | 190 | argv: []const []const u8, |
| 185 | 191 | cwd: ?[]const u8 = null, |
| 192 | cwd_dir: ?fs.Dir = null, | |
| 186 | 193 | env_map: ?*const BufMap = null, |
| 187 | 194 | max_output_bytes: usize = 50 * 1024, |
| 188 | 195 | expand_arg0: Arg0Expand = .no_expand, |
| ... | ... | @@ -194,6 +201,7 @@ pub const ChildProcess = struct { |
| 194 | 201 | child.stdout_behavior = .Pipe; |
| 195 | 202 | child.stderr_behavior = .Pipe; |
| 196 | 203 | child.cwd = args.cwd; |
| 204 | child.cwd_dir = args.cwd_dir; | |
| 197 | 205 | child.env_map = args.env_map; |
| 198 | 206 | child.expand_arg0 = args.expand_arg0; |
| 199 | 207 | |
| ... | ... | @@ -414,7 +422,9 @@ pub const ChildProcess = struct { |
| 414 | 422 | os.close(stderr_pipe[1]); |
| 415 | 423 | } |
| 416 | 424 | |
| 417 | if (self.cwd) |cwd| { | |
| 425 | if (self.cwd_dir) |cwd| { | |
| 426 | os.fchdir(cwd.fd) catch |err| forkChildErrReport(err_pipe[1], err); | |
| 427 | } else if (self.cwd) |cwd| { | |
| 418 | 428 | os.chdir(cwd) catch |err| forkChildErrReport(err_pipe[1], err); |
| 419 | 429 | } |
| 420 | 430 |
lib/std/fmt.zig+1-1| ... | ... | @@ -1058,7 +1058,7 @@ pub fn charToDigit(c: u8, radix: u8) (error{InvalidCharacter}!u8) { |
| 1058 | 1058 | return value; |
| 1059 | 1059 | } |
| 1060 | 1060 | |
| 1061 | fn digitToChar(digit: u8, uppercase: bool) u8 { | |
| 1061 | pub fn digitToChar(digit: u8, uppercase: bool) u8 { | |
| 1062 | 1062 | return switch (digit) { |
| 1063 | 1063 | 0...9 => digit + '0', |
| 1064 | 1064 | 10...35 => digit + ((if (uppercase) @as(u8, 'A') else @as(u8, 'a')) - 10), |
lib/std/fs.zig+22-4| ... | ... | @@ -606,7 +606,8 @@ pub const Dir = struct { |
| 606 | 606 | } else 0; |
| 607 | 607 | |
| 608 | 608 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; |
| 609 | const os_flags = lock_flag | O_LARGEFILE | os.O_CLOEXEC | if (flags.write and flags.read) | |
| 609 | const O_CLOEXEC: u32 = if (flags.share_with_child_process) 0 else os.O_CLOEXEC; | |
| 610 | const os_flags = lock_flag | O_LARGEFILE | O_CLOEXEC | if (flags.write and flags.read) | |
| 610 | 611 | @as(u32, os.O_RDWR) |
| 611 | 612 | else if (flags.write) |
| 612 | 613 | @as(u32, os.O_WRONLY) |
| ... | ... | @@ -689,7 +690,8 @@ pub const Dir = struct { |
| 689 | 690 | } else 0; |
| 690 | 691 | |
| 691 | 692 | const O_LARGEFILE = if (@hasDecl(os, "O_LARGEFILE")) os.O_LARGEFILE else 0; |
| 692 | const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | os.O_CLOEXEC | | |
| 693 | const O_CLOEXEC: u32 = if (flags.share_with_child_process) 0 else os.O_CLOEXEC; | |
| 694 | const os_flags = lock_flag | O_LARGEFILE | os.O_CREAT | O_CLOEXEC | | |
| 693 | 695 | (if (flags.truncate) @as(u32, os.O_TRUNC) else 0) | |
| 694 | 696 | (if (flags.read) @as(u32, os.O_RDWR) else os.O_WRONLY) | |
| 695 | 697 | (if (flags.exclusive) @as(u32, os.O_EXCL) else 0); |
| ... | ... | @@ -787,6 +789,15 @@ pub const Dir = struct { |
| 787 | 789 | } |
| 788 | 790 | } |
| 789 | 791 | |
| 792 | /// This function performs `makePath`, followed by `openDir`. | |
| 793 | /// If supported by the OS, this operation is atomic. It is not atomic on | |
| 794 | /// all operating systems. | |
| 795 | pub fn makeOpenPath(self: Dir, sub_path: []const u8, open_dir_options: OpenDirOptions) !Dir { | |
| 796 | // TODO improve this implementation on Windows; we can avoid 1 call to NtClose | |
| 797 | try self.makePath(sub_path); | |
| 798 | return self.openDir(sub_path, open_dir_options); | |
| 799 | } | |
| 800 | ||
| 790 | 801 | /// Changes the current working directory to the open directory handle. |
| 791 | 802 | /// This modifies global state and can have surprising effects in multi- |
| 792 | 803 | /// threaded applications. Most applications and especially libraries should |
| ... | ... | @@ -807,6 +818,11 @@ pub const Dir = struct { |
| 807 | 818 | /// `true` means the opened directory can be scanned for the files and sub-directories |
| 808 | 819 | /// of the result. It means the `iterate` function can be called. |
| 809 | 820 | iterate: bool = false, |
| 821 | ||
| 822 | /// `true` means the opened directory can be passed to a child process. | |
| 823 | /// `false` means the directory handle is considered to be closed when a child | |
| 824 | /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX. | |
| 825 | share_with_child_process: bool = false, | |
| 810 | 826 | }; |
| 811 | 827 | |
| 812 | 828 | /// Opens a directory at the given path. The directory is a system resource that remains |
| ... | ... | @@ -832,9 +848,11 @@ pub const Dir = struct { |
| 832 | 848 | return self.openDirW(&sub_path_w, args); |
| 833 | 849 | } else if (!args.iterate) { |
| 834 | 850 | const O_PATH = if (@hasDecl(os, "O_PATH")) os.O_PATH else 0; |
| 835 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC | O_PATH); | |
| 851 | const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC; | |
| 852 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC | O_PATH); | |
| 836 | 853 | } else { |
| 837 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | os.O_CLOEXEC); | |
| 854 | const O_CLOEXEC: u32 = if (args.share_with_child_process) 0 else os.O_CLOEXEC; | |
| 855 | return self.openDirFlagsZ(sub_path_c, os.O_DIRECTORY | os.O_RDONLY | O_CLOEXEC); | |
| 838 | 856 | } |
| 839 | 857 | } |
| 840 | 858 |
lib/std/fs/file.zig+10| ... | ... | @@ -69,6 +69,11 @@ pub const File = struct { |
| 69 | 69 | /// It allows the use of `noasync` when calling functions related to opening |
| 70 | 70 | /// the file, reading, and writing. |
| 71 | 71 | always_blocking: bool = false, |
| 72 | ||
| 73 | /// `true` means the opened directory can be passed to a child process. | |
| 74 | /// `false` means the directory handle is considered to be closed when a child | |
| 75 | /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX. | |
| 76 | share_with_child_process: bool = false, | |
| 72 | 77 | }; |
| 73 | 78 | |
| 74 | 79 | /// TODO https://github.com/ziglang/zig/issues/3802 |
| ... | ... | @@ -107,6 +112,11 @@ pub const File = struct { |
| 107 | 112 | /// For POSIX systems this is the file system mode the file will |
| 108 | 113 | /// be created with. |
| 109 | 114 | mode: Mode = default_mode, |
| 115 | ||
| 116 | /// `true` means the opened directory can be passed to a child process. | |
| 117 | /// `false` means the directory handle is considered to be closed when a child | |
| 118 | /// process is spawned. This corresponds to the inverse of `O_CLOEXEC` on POSIX. | |
| 119 | share_with_child_process: bool = false, | |
| 110 | 120 | }; |
| 111 | 121 | |
| 112 | 122 | /// Upon success, the stream is in an uninitialized state. To continue using it, |
lib/std/math.zig+37| ... | ... | @@ -986,6 +986,43 @@ pub const Order = enum { |
| 986 | 986 | |
| 987 | 987 | /// Greater than (`>`) |
| 988 | 988 | gt, |
| 989 | ||
| 990 | pub fn invert(self: Order) Order { | |
| 991 | return switch (self) { | |
| 992 | .lt => .gt, | |
| 993 | .eq => .eq, | |
| 994 | .gt => .gt, | |
| 995 | }; | |
| 996 | } | |
| 997 | ||
| 998 | pub fn compare(self: Order, op: CompareOperator) bool { | |
| 999 | return switch (self) { | |
| 1000 | .lt => switch (op) { | |
| 1001 | .lt => true, | |
| 1002 | .lte => true, | |
| 1003 | .eq => false, | |
| 1004 | .gte => false, | |
| 1005 | .gt => false, | |
| 1006 | .neq => true, | |
| 1007 | }, | |
| 1008 | .eq => switch (op) { | |
| 1009 | .lt => false, | |
| 1010 | .lte => true, | |
| 1011 | .eq => true, | |
| 1012 | .gte => true, | |
| 1013 | .gt => false, | |
| 1014 | .neq => false, | |
| 1015 | }, | |
| 1016 | .gt => switch (op) { | |
| 1017 | .lt => false, | |
| 1018 | .lte => false, | |
| 1019 | .eq => false, | |
| 1020 | .gte => true, | |
| 1021 | .gt => true, | |
| 1022 | .neq => true, | |
| 1023 | }, | |
| 1024 | }; | |
| 1025 | } | |
| 989 | 1026 | }; |
| 990 | 1027 | |
| 991 | 1028 | /// Given two numbers, this function returns the order they are with respect to each other. |
lib/std/math/big.zig+22-5| ... | ... | @@ -1,7 +1,24 @@ |
| 1 | pub usingnamespace @import("big/int.zig"); | |
| 2 | pub usingnamespace @import("big/rational.zig"); | |
| 1 | const std = @import("../std.zig"); | |
| 2 | const assert = std.debug.assert; | |
| 3 | 3 | |
| 4 | test "math.big" { | |
| 5 | _ = @import("big/int.zig"); | |
| 6 | _ = @import("big/rational.zig"); | |
| 4 | pub const Rational = @import("big/rational.zig").Rational; | |
| 5 | pub const int = @import("big/int.zig"); | |
| 6 | pub const Limb = usize; | |
| 7 | pub const DoubleLimb = std.meta.IntType(false, 2 * Limb.bit_count); | |
| 8 | pub const SignedDoubleLimb = std.meta.IntType(true, DoubleLimb.bit_count); | |
| 9 | pub const Log2Limb = std.math.Log2Int(Limb); | |
| 10 | ||
| 11 | comptime { | |
| 12 | assert(std.math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count); | |
| 13 | assert(Limb.bit_count <= 64); // u128 set is unsupported | |
| 14 | assert(Limb.is_signed == false); | |
| 15 | } | |
| 16 | ||
| 17 | test "" { | |
| 18 | _ = int; | |
| 19 | _ = Rational; | |
| 20 | _ = Limb; | |
| 21 | _ = DoubleLimb; | |
| 22 | _ = SignedDoubleLimb; | |
| 23 | _ = Log2Limb; | |
| 7 | 24 | } |
lib/std/math/big/int.zig+1681-2497| ... | ... | @@ -1,293 +1,196 @@ |
| 1 | 1 | const std = @import("../../std.zig"); |
| 2 | const debug = std.debug; | |
| 3 | const testing = std.testing; | |
| 4 | 2 | const math = std.math; |
| 3 | const Limb = std.math.big.Limb; | |
| 4 | const DoubleLimb = std.math.big.DoubleLimb; | |
| 5 | const SignedDoubleLimb = std.math.big.SignedDoubleLimb; | |
| 6 | const Log2Limb = std.math.big.Log2Limb; | |
| 7 | const Allocator = std.mem.Allocator; | |
| 5 | 8 | const mem = std.mem; |
| 6 | const Allocator = mem.Allocator; | |
| 7 | const ArrayList = std.ArrayList; | |
| 8 | 9 | const maxInt = std.math.maxInt; |
| 9 | 10 | const minInt = std.math.minInt; |
| 11 | const assert = std.debug.assert; | |
| 10 | 12 | |
| 11 | pub const Limb = usize; | |
| 12 | pub const DoubleLimb = std.meta.Int(false, 2 * Limb.bit_count); | |
| 13 | pub const SignedDoubleLimb = std.meta.Int(true, DoubleLimb.bit_count); | |
| 14 | pub const Log2Limb = math.Log2Int(Limb); | |
| 13 | /// Returns the number of limbs needed to store `scalar`, which must be a | |
| 14 | /// primitive integer value. | |
| 15 | pub fn calcLimbLen(scalar: var) usize { | |
| 16 | const T = @TypeOf(scalar); | |
| 17 | switch (@typeInfo(T)) { | |
| 18 | .Int => |info| { | |
| 19 | const UT = if (info.is_signed) std.meta.Int(false, info.bits - 1) else T; | |
| 20 | return @sizeOf(UT) / @sizeOf(Limb); | |
| 21 | }, | |
| 22 | .ComptimeInt => { | |
| 23 | const w_value = if (scalar < 0) -scalar else scalar; | |
| 24 | return @divFloor(math.log2(w_value), Limb.bit_count) + 1; | |
| 25 | }, | |
| 26 | else => @compileError("parameter must be a primitive integer type"), | |
| 27 | } | |
| 28 | } | |
| 15 | 29 | |
| 16 | comptime { | |
| 17 | debug.assert(math.floorPowerOfTwo(usize, Limb.bit_count) == Limb.bit_count); | |
| 18 | debug.assert(Limb.bit_count <= 64); // u128 set is unsupported | |
| 19 | debug.assert(Limb.is_signed == false); | |
| 30 | pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize { | |
| 31 | if (math.isPowerOfTwo(base)) | |
| 32 | return 0; | |
| 33 | return a_len + 2 + a_len + calcDivLimbsBufferLen(a_len, 1); | |
| 20 | 34 | } |
| 21 | 35 | |
| 22 | /// An arbitrary-precision big integer. | |
| 23 | /// | |
| 24 | /// Memory is allocated by an Int as needed to ensure operations never overflow. The range of an | |
| 25 | /// Int is bounded only by available memory. | |
| 26 | pub const Int = struct { | |
| 27 | const sign_bit: usize = 1 << (usize.bit_count - 1); | |
| 36 | pub fn calcDivLimbsBufferLen(a_len: usize, b_len: usize) usize { | |
| 37 | return calcMulLimbsBufferLen(a_len, b_len, 2) * 4; | |
| 38 | } | |
| 28 | 39 | |
| 29 | /// Default number of limbs to allocate on creation of an Int. | |
| 30 | pub const default_capacity = 4; | |
| 40 | pub fn calcMulLimbsBufferLen(a_len: usize, b_len: usize, aliases: usize) usize { | |
| 41 | return aliases * math.max(a_len, b_len); | |
| 42 | } | |
| 43 | ||
| 44 | pub fn calcSetStringLimbsBufferLen(base: u8, string_len: usize) usize { | |
| 45 | const limb_count = calcSetStringLimbCount(base, string_len); | |
| 46 | return calcMulLimbsBufferLen(limb_count, limb_count, 2); | |
| 47 | } | |
| 31 | 48 | |
| 32 | /// Allocator used by the Int when requesting memory. | |
| 33 | allocator: ?*Allocator, | |
| 49 | pub fn calcSetStringLimbCount(base: u8, string_len: usize) usize { | |
| 50 | return (string_len + (Limb.bit_count / base - 1)) / (Limb.bit_count / base); | |
| 51 | } | |
| 52 | ||
| 53 | /// a + b * c + *carry, sets carry to the overflow bits | |
| 54 | pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { | |
| 55 | @setRuntimeSafety(false); | |
| 56 | var r1: Limb = undefined; | |
| 57 | ||
| 58 | // r1 = a + *carry | |
| 59 | const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1)); | |
| 60 | ||
| 61 | // r2 = b * c | |
| 62 | const bc = @as(DoubleLimb, math.mulWide(Limb, b, c)); | |
| 63 | const r2 = @truncate(Limb, bc); | |
| 64 | const c2 = @truncate(Limb, bc >> Limb.bit_count); | |
| 65 | ||
| 66 | // r1 = r1 + r2 | |
| 67 | const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1)); | |
| 68 | ||
| 69 | // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then | |
| 70 | // c2 is at least <= maxInt(Limb) - 2. | |
| 71 | carry.* = c1 + c2 + c3; | |
| 72 | ||
| 73 | return r1; | |
| 74 | } | |
| 34 | 75 | |
| 76 | /// A arbitrary-precision big integer, with a fixed set of mutable limbs. | |
| 77 | pub const Mutable = struct { | |
| 35 | 78 | /// Raw digits. These are: |
| 36 | 79 | /// |
| 37 | 80 | /// * Little-endian ordered |
| 38 | 81 | /// * limbs.len >= 1 |
| 39 | /// * Zero is represent as Int.len() == 1 with limbs[0] == 0. | |
| 82 | /// * Zero is represented as limbs.len == 1 with limbs[0] == 0. | |
| 40 | 83 | /// |
| 41 | 84 | /// Accessing limbs directly should be avoided. |
| 85 | /// These are allocated limbs; the `len` field tells the valid range. | |
| 42 | 86 | limbs: []Limb, |
| 87 | len: usize, | |
| 88 | positive: bool, | |
| 43 | 89 | |
| 44 | /// High bit is the sign bit. If set, Int is negative, else Int is positive. | |
| 45 | /// The remaining bits represent the number of limbs used by Int. | |
| 46 | metadata: usize, | |
| 47 | ||
| 48 | /// Creates a new Int. default_capacity limbs will be allocated immediately. | |
| 49 | /// Int will be zeroed. | |
| 50 | pub fn init(allocator: *Allocator) !Int { | |
| 51 | return try Int.initCapacity(allocator, default_capacity); | |
| 52 | } | |
| 53 | ||
| 54 | /// Creates a new Int. Int will be set to `value`. | |
| 55 | /// | |
| 56 | /// This is identical to an `init`, followed by a `set`. | |
| 57 | pub fn initSet(allocator: *Allocator, value: var) !Int { | |
| 58 | var s = try Int.init(allocator); | |
| 59 | try s.set(value); | |
| 60 | return s; | |
| 90 | pub fn toConst(self: Mutable) Const { | |
| 91 | return .{ | |
| 92 | .limbs = self.limbs[0..self.len], | |
| 93 | .positive = self.positive, | |
| 94 | }; | |
| 61 | 95 | } |
| 62 | 96 | |
| 63 | /// Creates a new Int with a specific capacity. If capacity < default_capacity then the | |
| 64 | /// default capacity will be used instead. | |
| 65 | pub fn initCapacity(allocator: *Allocator, capacity: usize) !Int { | |
| 66 | return Int{ | |
| 97 | /// Asserts that the allocator owns the limbs memory. If this is not the case, | |
| 98 | /// use `toConst().toManaged()`. | |
| 99 | pub fn toManaged(self: Mutable, allocator: *Allocator) Managed { | |
| 100 | return .{ | |
| 67 | 101 | .allocator = allocator, |
| 68 | .metadata = 1, | |
| 69 | .limbs = block: { | |
| 70 | var limbs = try allocator.alloc(Limb, math.max(default_capacity, capacity)); | |
| 71 | limbs[0] = 0; | |
| 72 | break :block limbs; | |
| 73 | }, | |
| 102 | .limbs = limbs, | |
| 103 | .metadata = if (self.positive) | |
| 104 | self.len & ~Managed.sign_bit | |
| 105 | else | |
| 106 | self.len | Managed.sign_bit, | |
| 74 | 107 | }; |
| 75 | 108 | } |
| 76 | 109 | |
| 77 | /// Returns the number of limbs currently in use. | |
| 78 | pub fn len(self: Int) usize { | |
| 79 | return self.metadata & ~sign_bit; | |
| 80 | } | |
| 81 | ||
| 82 | /// Returns whether an Int is positive. | |
| 83 | pub fn isPositive(self: Int) bool { | |
| 84 | return self.metadata & sign_bit == 0; | |
| 85 | } | |
| 86 | ||
| 87 | /// Sets the sign of an Int. | |
| 88 | pub fn setSign(self: *Int, positive: bool) void { | |
| 89 | if (positive) { | |
| 90 | self.metadata &= ~sign_bit; | |
| 91 | } else { | |
| 92 | self.metadata |= sign_bit; | |
| 93 | } | |
| 94 | } | |
| 95 | ||
| 96 | /// Sets the length of an Int. | |
| 97 | /// | |
| 98 | /// If setLen is used, then the Int must be normalized to suit. | |
| 99 | pub fn setLen(self: *Int, new_len: usize) void { | |
| 100 | self.metadata &= sign_bit; | |
| 101 | self.metadata |= new_len; | |
| 102 | } | |
| 103 | ||
| 104 | /// Returns an Int backed by a fixed set of limb values. | |
| 105 | /// This is read-only and cannot be used as a result argument. If the Int tries to allocate | |
| 106 | /// memory a runtime panic will occur. | |
| 107 | pub fn initFixed(limbs: []const Limb) Int { | |
| 108 | var self = Int{ | |
| 109 | .allocator = null, | |
| 110 | .metadata = limbs.len, | |
| 111 | // Cast away the const, invalid use to pass as a pointer argument. | |
| 112 | .limbs = @intToPtr([*]Limb, @ptrToInt(limbs.ptr))[0..limbs.len], | |
| 110 | /// `value` is a primitive integer type. | |
| 111 | /// Asserts the value fits within the provided `limbs_buffer`. | |
| 112 | /// Note: `calcLimbLen` can be used to figure out how big an array to allocate for `limbs_buffer`. | |
| 113 | pub fn init(limbs_buffer: []Limb, value: var) Mutable { | |
| 114 | limbs_buffer[0] = 0; | |
| 115 | var self: Mutable = .{ | |
| 116 | .limbs = limbs_buffer, | |
| 117 | .len = 1, | |
| 118 | .positive = true, | |
| 113 | 119 | }; |
| 114 | ||
| 115 | self.normalize(limbs.len); | |
| 120 | self.set(value); | |
| 116 | 121 | return self; |
| 117 | 122 | } |
| 118 | 123 | |
| 119 | /// Ensures an Int has enough space allocated for capacity limbs. If the Int does not have | |
| 120 | /// sufficient capacity, the exact amount will be allocated. This occurs even if the requested | |
| 121 | /// capacity is only greater than the current capacity by one limb. | |
| 122 | pub fn ensureCapacity(self: *Int, capacity: usize) !void { | |
| 123 | self.assertWritable(); | |
| 124 | if (capacity <= self.limbs.len) { | |
| 125 | return; | |
| 126 | } | |
| 127 | ||
| 128 | self.limbs = try self.allocator.?.realloc(self.limbs, capacity); | |
| 129 | } | |
| 130 | ||
| 131 | fn assertWritable(self: Int) void { | |
| 132 | if (self.allocator == null) { | |
| 133 | @panic("provided Int value is read-only but must be writable"); | |
| 134 | } | |
| 135 | } | |
| 136 | ||
| 137 | /// Frees all memory associated with an Int. | |
| 138 | pub fn deinit(self: Int) void { | |
| 139 | self.assertWritable(); | |
| 140 | self.allocator.?.free(self.limbs); | |
| 141 | } | |
| 142 | ||
| 143 | /// Clones an Int and returns a new Int with the same value. The new Int is a deep copy and | |
| 144 | /// can be modified separately from the original. | |
| 145 | pub fn clone(other: Int) !Int { | |
| 146 | return other.clone2(other.allocator.?); | |
| 147 | } | |
| 148 | ||
| 149 | pub fn clone2(other: Int, allocator: *Allocator) !Int { | |
| 150 | return Int{ | |
| 151 | .allocator = allocator, | |
| 152 | .metadata = other.metadata, | |
| 153 | .limbs = block: { | |
| 154 | var limbs = try allocator.alloc(Limb, other.len()); | |
| 155 | mem.copy(Limb, limbs[0..], other.limbs[0..other.len()]); | |
| 156 | break :block limbs; | |
| 157 | }, | |
| 158 | }; | |
| 159 | } | |
| 160 | ||
| 161 | /// Copies the value of an Int to an existing Int so that they both have the same value. | |
| 162 | /// Extra memory will be allocated if the receiver does not have enough capacity. | |
| 163 | pub fn copy(self: *Int, other: Int) !void { | |
| 164 | self.assertWritable(); | |
| 165 | if (self.limbs.ptr == other.limbs.ptr) { | |
| 166 | return; | |
| 124 | /// Copies the value of a Const to an existing Mutable so that they both have the same value. | |
| 125 | /// Asserts the value fits in the limbs buffer. | |
| 126 | pub fn copy(self: *Mutable, other: Const) void { | |
| 127 | if (self.limbs.ptr != other.limbs.ptr) { | |
| 128 | mem.copy(Limb, self.limbs[0..], other.limbs[0..other.limbs.len]); | |
| 167 | 129 | } |
| 168 | ||
| 169 | try self.ensureCapacity(other.len()); | |
| 170 | mem.copy(Limb, self.limbs[0..], other.limbs[0..other.len()]); | |
| 171 | self.metadata = other.metadata; | |
| 130 | self.positive = other.positive; | |
| 131 | self.len = other.limbs.len; | |
| 172 | 132 | } |
| 173 | 133 | |
| 174 | /// Efficiently swap an Int with another. This swaps the limb pointers and a full copy is not | |
| 134 | /// Efficiently swap an Mutable with another. This swaps the limb pointers and a full copy is not | |
| 175 | 135 | /// performed. The address of the limbs field will not be the same after this function. |
| 176 | pub fn swap(self: *Int, other: *Int) void { | |
| 177 | self.assertWritable(); | |
| 178 | mem.swap(Int, self, other); | |
| 179 | } | |
| 180 | ||
| 181 | pub fn dump(self: Int) void { | |
| 182 | for (self.limbs) |limb| { | |
| 183 | debug.warn("{x} ", .{limb}); | |
| 184 | } | |
| 185 | debug.warn("\n", .{}); | |
| 186 | } | |
| 187 | ||
| 188 | /// Negate the sign of an Int. | |
| 189 | pub fn negate(self: *Int) void { | |
| 190 | self.metadata ^= sign_bit; | |
| 191 | } | |
| 192 | ||
| 193 | /// Make an Int positive. | |
| 194 | pub fn abs(self: *Int) void { | |
| 195 | self.metadata &= ~sign_bit; | |
| 196 | } | |
| 197 | ||
| 198 | /// Returns true if an Int is odd. | |
| 199 | pub fn isOdd(self: Int) bool { | |
| 200 | return self.limbs[0] & 1 != 0; | |
| 201 | } | |
| 202 | ||
| 203 | /// Returns true if an Int is even. | |
| 204 | pub fn isEven(self: Int) bool { | |
| 205 | return !self.isOdd(); | |
| 206 | } | |
| 207 | ||
| 208 | /// Returns the number of bits required to represent the absolute value an Int. | |
| 209 | fn bitCountAbs(self: Int) usize { | |
| 210 | return (self.len() - 1) * Limb.bit_count + (Limb.bit_count - @clz(Limb, self.limbs[self.len() - 1])); | |
| 136 | pub fn swap(self: *Mutable, other: *Mutable) void { | |
| 137 | mem.swap(Mutable, self, other); | |
| 211 | 138 | } |
| 212 | 139 | |
| 213 | /// Returns the number of bits required to represent the integer in twos-complement form. | |
| 214 | /// | |
| 215 | /// If the integer is negative the value returned is the number of bits needed by a signed | |
| 216 | /// integer to represent the value. If positive the value is the number of bits for an | |
| 217 | /// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount | |
| 218 | /// one greater than the returned value. | |
| 219 | /// | |
| 220 | /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7. | |
| 221 | fn bitCountTwosComp(self: Int) usize { | |
| 222 | var bits = self.bitCountAbs(); | |
| 223 | ||
| 224 | // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos | |
| 225 | // complement requires one less bit. | |
| 226 | if (!self.isPositive()) block: { | |
| 227 | bits += 1; | |
| 228 | ||
| 229 | if (@popCount(Limb, self.limbs[self.len() - 1]) == 1) { | |
| 230 | for (self.limbs[0 .. self.len() - 1]) |limb| { | |
| 231 | if (@popCount(Limb, limb) != 0) { | |
| 232 | break :block; | |
| 233 | } | |
| 234 | } | |
| 235 | ||
| 236 | bits -= 1; | |
| 237 | } | |
| 140 | pub fn dump(self: Mutable) void { | |
| 141 | for (self.limbs[0..self.len]) |limb| { | |
| 142 | std.debug.warn("{x} ", .{limb}); | |
| 238 | 143 | } |
| 239 | ||
| 240 | return bits; | |
| 144 | std.debug.warn("capacity={} positive={}\n", .{ self.limbs.len, self.positive }); | |
| 241 | 145 | } |
| 242 | 146 | |
| 243 | pub fn fitsInTwosComp(self: Int, is_signed: bool, bit_count: usize) bool { | |
| 244 | if (self.eqZero()) { | |
| 245 | return true; | |
| 246 | } | |
| 247 | if (!is_signed and !self.isPositive()) { | |
| 248 | return false; | |
| 249 | } | |
| 250 | ||
| 251 | const req_bits = self.bitCountTwosComp() + @boolToInt(self.isPositive() and is_signed); | |
| 252 | return bit_count >= req_bits; | |
| 147 | /// Clones an Mutable and returns a new Mutable with the same value. The new Mutable is a deep copy and | |
| 148 | /// can be modified separately from the original. | |
| 149 | /// Asserts that limbs is big enough to store the value. | |
| 150 | pub fn clone(other: Mutable, limbs: []Limb) Mutable { | |
| 151 | mem.copy(Limb, limbs, other.limbs[0..other.len]); | |
| 152 | return .{ | |
| 153 | .limbs = limbs, | |
| 154 | .len = other.len, | |
| 155 | .positive = other.positive, | |
| 156 | }; | |
| 253 | 157 | } |
| 254 | 158 | |
| 255 | /// Returns whether self can fit into an integer of the requested type. | |
| 256 | pub fn fits(self: Int, comptime T: type) bool { | |
| 257 | return self.fitsInTwosComp(T.is_signed, T.bit_count); | |
| 159 | pub fn negate(self: *Mutable) void { | |
| 160 | self.positive = !self.positive; | |
| 258 | 161 | } |
| 259 | 162 | |
| 260 | /// Returns the approximate size of the integer in the given base. Negative values accommodate for | |
| 261 | /// the minus sign. This is used for determining the number of characters needed to print the | |
| 262 | /// value. It is inexact and may exceed the given value by ~1-2 bytes. | |
| 263 | pub fn sizeInBase(self: Int, base: usize) usize { | |
| 264 | const bit_count = @as(usize, @boolToInt(!self.isPositive())) + self.bitCountAbs(); | |
| 265 | return (bit_count / math.log2(base)) + 1; | |
| 163 | /// Modify to become the absolute value | |
| 164 | pub fn abs(self: *Mutable) void { | |
| 165 | self.positive = true; | |
| 266 | 166 | } |
| 267 | 167 | |
| 268 | /// Sets an Int to value. Value must be an primitive integer type. | |
| 269 | pub fn set(self: *Int, value: var) Allocator.Error!void { | |
| 270 | self.assertWritable(); | |
| 168 | /// Sets the Mutable to value. Value must be an primitive integer type. | |
| 169 | /// Asserts the value fits within the limbs buffer. | |
| 170 | /// Note: `calcLimbLen` can be used to figure out how big the limbs buffer | |
| 171 | /// needs to be to store a specific value. | |
| 172 | pub fn set(self: *Mutable, value: var) void { | |
| 271 | 173 | const T = @TypeOf(value); |
| 272 | 174 | |
| 273 | 175 | switch (@typeInfo(T)) { |
| 274 | 176 | .Int => |info| { |
| 275 | 177 | const UT = if (T.is_signed) std.meta.Int(false, T.bit_count - 1) else T; |
| 276 | 178 | |
| 277 | try self.ensureCapacity(@sizeOf(UT) / @sizeOf(Limb)); | |
| 278 | self.metadata = 0; | |
| 279 | self.setSign(value >= 0); | |
| 179 | const needed_limbs = @sizeOf(UT) / @sizeOf(Limb); | |
| 180 | assert(needed_limbs <= self.limbs.len); // value too big | |
| 181 | self.len = 0; | |
| 182 | self.positive = value >= 0; | |
| 280 | 183 | |
| 281 | 184 | var w_value: UT = if (value < 0) @intCast(UT, -value) else @intCast(UT, value); |
| 282 | 185 | |
| 283 | 186 | if (info.bits <= Limb.bit_count) { |
| 284 | 187 | self.limbs[0] = @as(Limb, w_value); |
| 285 | self.metadata += 1; | |
| 188 | self.len += 1; | |
| 286 | 189 | } else { |
| 287 | 190 | var i: usize = 0; |
| 288 | 191 | while (w_value != 0) : (i += 1) { |
| 289 | 192 | self.limbs[i] = @truncate(Limb, w_value); |
| 290 | self.metadata += 1; | |
| 193 | self.len += 1; | |
| 291 | 194 | |
| 292 | 195 | // TODO: shift == 64 at compile-time fails. Fails on u128 limbs. |
| 293 | 196 | w_value >>= Limb.bit_count / 2; |
| ... | ... | @@ -299,10 +202,10 @@ pub const Int = struct { |
| 299 | 202 | comptime var w_value = if (value < 0) -value else value; |
| 300 | 203 | |
| 301 | 204 | const req_limbs = @divFloor(math.log2(w_value), Limb.bit_count) + 1; |
| 302 | try self.ensureCapacity(req_limbs); | |
| 205 | assert(req_limbs <= self.limbs.len); // value too big | |
| 303 | 206 | |
| 304 | self.metadata = req_limbs; | |
| 305 | self.setSign(value >= 0); | |
| 207 | self.len = req_limbs; | |
| 208 | self.positive = value >= 0; | |
| 306 | 209 | |
| 307 | 210 | if (w_value <= maxInt(Limb)) { |
| 308 | 211 | self.limbs[0] = w_value; |
| ... | ... | @@ -318,98 +221,35 @@ pub const Int = struct { |
| 318 | 221 | } |
| 319 | 222 | } |
| 320 | 223 | }, |
| 321 | else => { | |
| 322 | @compileError("cannot set Int using type " ++ @typeName(T)); | |
| 323 | }, | |
| 324 | } | |
| 325 | } | |
| 326 | ||
| 327 | pub const ConvertError = error{ | |
| 328 | NegativeIntoUnsigned, | |
| 329 | TargetTooSmall, | |
| 330 | }; | |
| 331 | ||
| 332 | /// Convert self to type T. | |
| 333 | /// | |
| 334 | /// Returns an error if self cannot be narrowed into the requested type without truncation. | |
| 335 | pub fn to(self: Int, comptime T: type) ConvertError!T { | |
| 336 | switch (@typeInfo(T)) { | |
| 337 | .Int => { | |
| 338 | const UT = std.meta.Int(false, T.bit_count); | |
| 339 | ||
| 340 | if (self.bitCountTwosComp() > T.bit_count) { | |
| 341 | return error.TargetTooSmall; | |
| 342 | } | |
| 343 | ||
| 344 | var r: UT = 0; | |
| 345 | ||
| 346 | if (@sizeOf(UT) <= @sizeOf(Limb)) { | |
| 347 | r = @intCast(UT, self.limbs[0]); | |
| 348 | } else { | |
| 349 | for (self.limbs[0..self.len()]) |_, ri| { | |
| 350 | const limb = self.limbs[self.len() - ri - 1]; | |
| 351 | r <<= Limb.bit_count; | |
| 352 | r |= limb; | |
| 353 | } | |
| 354 | } | |
| 355 | ||
| 356 | if (!T.is_signed) { | |
| 357 | return if (self.isPositive()) @intCast(T, r) else error.NegativeIntoUnsigned; | |
| 358 | } else { | |
| 359 | if (self.isPositive()) { | |
| 360 | return @intCast(T, r); | |
| 361 | } else { | |
| 362 | if (math.cast(T, r)) |ok| { | |
| 363 | return -ok; | |
| 364 | } else |_| { | |
| 365 | return minInt(T); | |
| 366 | } | |
| 367 | } | |
| 368 | } | |
| 369 | }, | |
| 370 | else => { | |
| 371 | @compileError("cannot convert Int to type " ++ @typeName(T)); | |
| 372 | }, | |
| 224 | else => @compileError("cannot set Mutable using type " ++ @typeName(T)), | |
| 373 | 225 | } |
| 374 | 226 | } |
| 375 | 227 | |
| 376 | fn charToDigit(ch: u8, base: u8) !u8 { | |
| 377 | const d = switch (ch) { | |
| 378 | '0'...'9' => ch - '0', | |
| 379 | 'a'...'f' => (ch - 'a') + 0xa, | |
| 380 | 'A'...'F' => (ch - 'A') + 0xa, | |
| 381 | else => return error.InvalidCharForDigit, | |
| 382 | }; | |
| 383 | ||
| 384 | return if (d < base) d else return error.DigitTooLargeForBase; | |
| 385 | } | |
| 386 | ||
| 387 | fn digitToChar(d: u8, base: u8, uppercase: bool) !u8 { | |
| 388 | if (d >= base) { | |
| 389 | return error.DigitTooLargeForBase; | |
| 390 | } | |
| 391 | ||
| 392 | const a: u8 = if (uppercase) 'A' else 'a'; | |
| 393 | return switch (d) { | |
| 394 | 0...9 => '0' + d, | |
| 395 | 0xa...0xf => (a - 0xa) + d, | |
| 396 | else => unreachable, | |
| 397 | }; | |
| 398 | } | |
| 399 | ||
| 400 | 228 | /// Set self from the string representation `value`. |
| 401 | 229 | /// |
| 402 | 230 | /// `value` must contain only digits <= `base` and is case insensitive. Base prefixes are |
| 403 | 231 | /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are |
| 404 | 232 | /// ignored and can be used as digit separators. |
| 405 | 233 | /// |
| 406 | /// Returns an error if memory could not be allocated or `value` has invalid digits for the | |
| 407 | /// requested base. | |
| 408 | pub fn setString(self: *Int, base: u8, value: []const u8) !void { | |
| 409 | self.assertWritable(); | |
| 410 | if (base < 2 or base > 16) { | |
| 411 | return error.InvalidBase; | |
| 412 | } | |
| 234 | /// Asserts there is enough memory for the value in `self.limbs`. An upper bound on number of limbs can | |
| 235 | /// be determined with `calcSetStringLimbCount`. | |
| 236 | /// Asserts the base is in the range [2, 16]. | |
| 237 | /// | |
| 238 | /// Returns an error if the value has invalid digits for the requested base. | |
| 239 | /// | |
| 240 | /// `limbs_buffer` is used for temporary storage. The size required can be found with | |
| 241 | /// `calcSetStringLimbsBufferLen`. | |
| 242 | /// | |
| 243 | /// If `allocator` is provided, it will be used for temporary storage to improve | |
| 244 | /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm. | |
| 245 | pub fn setString( | |
| 246 | self: *Mutable, | |
| 247 | base: u8, | |
| 248 | value: []const u8, | |
| 249 | limbs_buffer: []Limb, | |
| 250 | allocator: ?*Allocator, | |
| 251 | ) error{InvalidCharacter}!void { | |
| 252 | assert(base >= 2 and base <= 16); | |
| 413 | 253 | |
| 414 | 254 | var i: usize = 0; |
| 415 | 255 | var positive = true; |
| ... | ... | @@ -418,753 +258,561 @@ pub const Int = struct { |
| 418 | 258 | i += 1; |
| 419 | 259 | } |
| 420 | 260 | |
| 421 | const ap_base = Int.initFixed(([_]Limb{base})[0..]); | |
| 422 | try self.set(0); | |
| 261 | const ap_base: Const = .{ .limbs = &[_]Limb{base}, .positive = true }; | |
| 262 | self.set(0); | |
| 423 | 263 | |
| 424 | 264 | for (value[i..]) |ch| { |
| 425 | 265 | if (ch == '_') { |
| 426 | 266 | continue; |
| 427 | 267 | } |
| 428 | const d = try charToDigit(ch, base); | |
| 429 | ||
| 430 | const ap_d = Int.initFixed(([_]Limb{d})[0..]); | |
| 268 | const d = try std.fmt.charToDigit(ch, base); | |
| 269 | const ap_d: Const = .{ .limbs = &[_]Limb{d}, .positive = true }; | |
| 431 | 270 | |
| 432 | try self.mul(self.*, ap_base); | |
| 433 | try self.add(self.*, ap_d); | |
| 271 | self.mul(self.toConst(), ap_base, limbs_buffer, allocator); | |
| 272 | self.add(self.toConst(), ap_d); | |
| 434 | 273 | } |
| 435 | self.setSign(positive); | |
| 274 | self.positive = positive; | |
| 436 | 275 | } |
| 437 | 276 | |
| 438 | /// Converts self to a string in the requested base. Memory is allocated from the provided | |
| 439 | /// allocator and not the one present in self. | |
| 440 | /// TODO make this call format instead of the other way around | |
| 441 | pub fn toString(self: Int, allocator: *Allocator, base: u8, uppercase: bool) ![]const u8 { | |
| 442 | if (base < 2 or base > 16) { | |
| 443 | return error.InvalidBase; | |
| 444 | } | |
| 445 | ||
| 446 | var digits = ArrayList(u8).init(allocator); | |
| 447 | try digits.ensureCapacity(self.sizeInBase(base) + 1); | |
| 448 | defer digits.deinit(); | |
| 277 | /// r = a + scalar | |
| 278 | /// | |
| 279 | /// r and a may be aliases. | |
| 280 | /// scalar is a primitive integer type. | |
| 281 | /// | |
| 282 | /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by | |
| 283 | /// r is `math.max(a.limbs.len, calcLimbLen(scalar)) + 1`. | |
| 284 | pub fn addScalar(r: *Mutable, a: Const, scalar: var) void { | |
| 285 | var limbs: [calcLimbLen(scalar)]Limb = undefined; | |
| 286 | const operand = init(&limbs, scalar).toConst(); | |
| 287 | return add(r, a, operand); | |
| 288 | } | |
| 449 | 289 | |
| 450 | if (self.eqZero()) { | |
| 451 | try digits.append('0'); | |
| 452 | return digits.toOwnedSlice(); | |
| 290 | /// r = a + b | |
| 291 | /// | |
| 292 | /// r, a and b may be aliases. | |
| 293 | /// | |
| 294 | /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by | |
| 295 | /// r is `math.max(a.limbs.len, b.limbs.len) + 1`. | |
| 296 | pub fn add(r: *Mutable, a: Const, b: Const) void { | |
| 297 | if (a.eqZero()) { | |
| 298 | r.copy(b); | |
| 299 | return; | |
| 300 | } else if (b.eqZero()) { | |
| 301 | r.copy(a); | |
| 302 | return; | |
| 453 | 303 | } |
| 454 | 304 | |
| 455 | // Power of two: can do a single pass and use masks to extract digits. | |
| 456 | if (math.isPowerOfTwo(base)) { | |
| 457 | const base_shift = math.log2_int(Limb, base); | |
| 458 | ||
| 459 | for (self.limbs[0..self.len()]) |limb| { | |
| 460 | var shift: usize = 0; | |
| 461 | while (shift < Limb.bit_count) : (shift += base_shift) { | |
| 462 | const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & @as(Limb, base - 1)); | |
| 463 | const ch = try digitToChar(r, base, uppercase); | |
| 464 | try digits.append(ch); | |
| 465 | } | |
| 305 | if (a.limbs.len == 1 and b.limbs.len == 1 and a.positive == b.positive) { | |
| 306 | if (!@addWithOverflow(Limb, a.limbs[0], b.limbs[0], &r.limbs[0])) { | |
| 307 | r.len = 1; | |
| 308 | r.positive = a.positive; | |
| 309 | return; | |
| 466 | 310 | } |
| 311 | } | |
| 467 | 312 | |
| 468 | while (true) { | |
| 469 | // always will have a non-zero digit somewhere | |
| 470 | const c = digits.pop(); | |
| 471 | if (c != '0') { | |
| 472 | digits.append(c) catch unreachable; | |
| 473 | break; | |
| 474 | } | |
| 313 | if (a.positive != b.positive) { | |
| 314 | if (a.positive) { | |
| 315 | // (a) + (-b) => a - b | |
| 316 | r.sub(a, b.abs()); | |
| 317 | } else { | |
| 318 | // (-a) + (b) => b - a | |
| 319 | r.sub(b, a.abs()); | |
| 475 | 320 | } |
| 476 | 321 | } else { |
| 477 | // Non power-of-two: batch divisions per word size. | |
| 478 | const digits_per_limb = math.log(Limb, base, maxInt(Limb)); | |
| 479 | var limb_base: Limb = 1; | |
| 480 | var j: usize = 0; | |
| 481 | while (j < digits_per_limb) : (j += 1) { | |
| 482 | limb_base *= base; | |
| 322 | if (a.limbs.len >= b.limbs.len) { | |
| 323 | lladd(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); | |
| 324 | r.normalize(a.limbs.len + 1); | |
| 325 | } else { | |
| 326 | lladd(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); | |
| 327 | r.normalize(b.limbs.len + 1); | |
| 483 | 328 | } |
| 484 | 329 | |
| 485 | var q = try self.clone2(allocator); | |
| 486 | defer q.deinit(); | |
| 487 | q.abs(); | |
| 488 | var r = try Int.init(allocator); | |
| 489 | defer r.deinit(); | |
| 490 | var b = try Int.initSet(allocator, limb_base); | |
| 491 | defer b.deinit(); | |
| 492 | ||
| 493 | while (q.len() >= 2) { | |
| 494 | try Int.divTrunc(&q, &r, q, b); | |
| 330 | r.positive = a.positive; | |
| 331 | } | |
| 332 | } | |
| 495 | 333 | |
| 496 | var r_word = r.limbs[0]; | |
| 497 | var i: usize = 0; | |
| 498 | while (i < digits_per_limb) : (i += 1) { | |
| 499 | const ch = try digitToChar(@intCast(u8, r_word % base), base, uppercase); | |
| 500 | r_word /= base; | |
| 501 | try digits.append(ch); | |
| 334 | /// r = a - b | |
| 335 | /// | |
| 336 | /// r, a and b may be aliases. | |
| 337 | /// | |
| 338 | /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by | |
| 339 | /// r is `math.max(a.limbs.len, b.limbs.len) + 1`. The +1 is not needed if both operands are positive. | |
| 340 | pub fn sub(r: *Mutable, a: Const, b: Const) void { | |
| 341 | if (a.positive != b.positive) { | |
| 342 | if (a.positive) { | |
| 343 | // (a) - (-b) => a + b | |
| 344 | r.add(a, b.abs()); | |
| 345 | } else { | |
| 346 | // (-a) - (b) => -(a + b) | |
| 347 | r.add(a.abs(), b); | |
| 348 | r.positive = false; | |
| 349 | } | |
| 350 | } else { | |
| 351 | if (a.positive) { | |
| 352 | // (a) - (b) => a - b | |
| 353 | if (a.order(b) != .lt) { | |
| 354 | llsub(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); | |
| 355 | r.normalize(a.limbs.len); | |
| 356 | r.positive = true; | |
| 357 | } else { | |
| 358 | llsub(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); | |
| 359 | r.normalize(b.limbs.len); | |
| 360 | r.positive = false; | |
| 361 | } | |
| 362 | } else { | |
| 363 | // (-a) - (-b) => -(a - b) | |
| 364 | if (a.order(b) == .lt) { | |
| 365 | llsub(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); | |
| 366 | r.normalize(a.limbs.len); | |
| 367 | r.positive = false; | |
| 368 | } else { | |
| 369 | llsub(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); | |
| 370 | r.normalize(b.limbs.len); | |
| 371 | r.positive = true; | |
| 502 | 372 | } |
| 503 | 373 | } |
| 374 | } | |
| 375 | } | |
| 504 | 376 | |
| 505 | { | |
| 506 | debug.assert(q.len() == 1); | |
| 507 | ||
| 508 | var r_word = q.limbs[0]; | |
| 509 | while (r_word != 0) { | |
| 510 | const ch = try digitToChar(@intCast(u8, r_word % base), base, uppercase); | |
| 511 | r_word /= base; | |
| 512 | try digits.append(ch); | |
| 513 | } | |
| 514 | } | |
| 515 | } | |
| 516 | ||
| 517 | if (!self.isPositive()) { | |
| 518 | try digits.append('-'); | |
| 519 | } | |
| 520 | ||
| 521 | var s = digits.toOwnedSlice(); | |
| 522 | mem.reverse(u8, s); | |
| 523 | return s; | |
| 524 | } | |
| 377 | /// rma = a * b | |
| 378 | /// | |
| 379 | /// `rma` may alias with `a` or `b`. | |
| 380 | /// `a` and `b` may alias with each other. | |
| 381 | /// | |
| 382 | /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by | |
| 383 | /// rma is given by `a.limbs.len + b.limbs.len + 1`. | |
| 384 | /// | |
| 385 | /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcMulLimbsBufferLen`. | |
| 386 | pub fn mul(rma: *Mutable, a: Const, b: Const, limbs_buffer: []Limb, allocator: ?*Allocator) void { | |
| 387 | var buf_index: usize = 0; | |
| 525 | 388 | |
| 526 | /// To allow `std.fmt.printf` to work with Int. | |
| 527 | /// TODO make this non-allocating | |
| 528 | /// TODO support read-only fixed integers | |
| 529 | pub fn format( | |
| 530 | self: Int, | |
| 531 | comptime fmt: []const u8, | |
| 532 | options: std.fmt.FormatOptions, | |
| 533 | out_stream: var, | |
| 534 | ) !void { | |
| 535 | comptime var radix = 10; | |
| 536 | comptime var uppercase = false; | |
| 389 | const a_copy = if (rma.limbs.ptr == a.limbs.ptr) blk: { | |
| 390 | const start = buf_index; | |
| 391 | mem.copy(Limb, limbs_buffer[buf_index..], a.limbs); | |
| 392 | buf_index += a.limbs.len; | |
| 393 | break :blk a.toMutable(limbs_buffer[start..buf_index]).toConst(); | |
| 394 | } else a; | |
| 537 | 395 | |
| 538 | if (fmt.len == 0 or comptime std.mem.eql(u8, fmt, "d")) { | |
| 539 | radix = 10; | |
| 540 | uppercase = false; | |
| 541 | } else if (comptime std.mem.eql(u8, fmt, "b")) { | |
| 542 | radix = 2; | |
| 543 | uppercase = false; | |
| 544 | } else if (comptime std.mem.eql(u8, fmt, "x")) { | |
| 545 | radix = 16; | |
| 546 | uppercase = false; | |
| 547 | } else if (comptime std.mem.eql(u8, fmt, "X")) { | |
| 548 | radix = 16; | |
| 549 | uppercase = true; | |
| 550 | } else { | |
| 551 | @compileError("Unknown format string: '" ++ fmt ++ "'"); | |
| 552 | } | |
| 396 | const b_copy = if (rma.limbs.ptr == b.limbs.ptr) blk: { | |
| 397 | const start = buf_index; | |
| 398 | mem.copy(Limb, limbs_buffer[buf_index..], b.limbs); | |
| 399 | buf_index += b.limbs.len; | |
| 400 | break :blk b.toMutable(limbs_buffer[start..buf_index]).toConst(); | |
| 401 | } else b; | |
| 553 | 402 | |
| 554 | var buf: [4096]u8 = undefined; | |
| 555 | var fba = std.heap.FixedBufferAllocator.init(&buf); | |
| 556 | const str = self.toString(&fba.allocator, radix, uppercase) catch @panic("TODO make this non allocating"); | |
| 557 | return out_stream.writeAll(str); | |
| 403 | return rma.mulNoAlias(a_copy, b_copy, allocator); | |
| 558 | 404 | } |
| 559 | 405 | |
| 560 | /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| == | |
| 561 | /// |b| or |a| > |b| respectively. | |
| 562 | pub fn cmpAbs(a: Int, b: Int) math.Order { | |
| 563 | if (a.len() < b.len()) { | |
| 564 | return .lt; | |
| 565 | } | |
| 566 | if (a.len() > b.len()) { | |
| 567 | return .gt; | |
| 568 | } | |
| 569 | ||
| 570 | var i: usize = a.len() - 1; | |
| 571 | while (i != 0) : (i -= 1) { | |
| 572 | if (a.limbs[i] != b.limbs[i]) { | |
| 573 | break; | |
| 406 | /// rma = a * b | |
| 407 | /// | |
| 408 | /// `rma` may not alias with `a` or `b`. | |
| 409 | /// `a` and `b` may alias with each other. | |
| 410 | /// | |
| 411 | /// Asserts the result fits in `rma`. An upper bound on the number of limbs needed by | |
| 412 | /// rma is given by `a.limbs.len + b.limbs.len + 1`. | |
| 413 | /// | |
| 414 | /// If `allocator` is provided, it will be used for temporary storage to improve | |
| 415 | /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm. | |
| 416 | pub fn mulNoAlias(rma: *Mutable, a: Const, b: Const, allocator: ?*Allocator) void { | |
| 417 | assert(rma.limbs.ptr != a.limbs.ptr); // illegal aliasing | |
| 418 | assert(rma.limbs.ptr != b.limbs.ptr); // illegal aliasing | |
| 419 | ||
| 420 | if (a.limbs.len == 1 and b.limbs.len == 1) { | |
| 421 | if (!@mulWithOverflow(Limb, a.limbs[0], b.limbs[0], &rma.limbs[0])) { | |
| 422 | rma.len = 1; | |
| 423 | rma.positive = (a.positive == b.positive); | |
| 424 | return; | |
| 574 | 425 | } |
| 575 | 426 | } |
| 576 | 427 | |
| 577 | if (a.limbs[i] < b.limbs[i]) { | |
| 578 | return .lt; | |
| 579 | } else if (a.limbs[i] > b.limbs[i]) { | |
| 580 | return .gt; | |
| 581 | } else { | |
| 582 | return .eq; | |
| 583 | } | |
| 584 | } | |
| 585 | ||
| 586 | /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a | |
| 587 | /// > b respectively. | |
| 588 | pub fn cmp(a: Int, b: Int) math.Order { | |
| 589 | if (a.isPositive() != b.isPositive()) { | |
| 590 | return if (a.isPositive()) .gt else .lt; | |
| 591 | } else { | |
| 592 | const r = cmpAbs(a, b); | |
| 593 | return if (a.isPositive()) r else switch (r) { | |
| 594 | .lt => math.Order.gt, | |
| 595 | .eq => math.Order.eq, | |
| 596 | .gt => math.Order.lt, | |
| 597 | }; | |
| 598 | } | |
| 599 | } | |
| 600 | ||
| 601 | /// Returns true if a == 0. | |
| 602 | pub fn eqZero(a: Int) bool { | |
| 603 | return a.len() == 1 and a.limbs[0] == 0; | |
| 604 | } | |
| 428 | mem.set(Limb, rma.limbs[0 .. a.limbs.len + b.limbs.len + 1], 0); | |
| 605 | 429 | |
| 606 | /// Returns true if |a| == |b|. | |
| 607 | pub fn eqAbs(a: Int, b: Int) bool { | |
| 608 | return cmpAbs(a, b) == .eq; | |
| 609 | } | |
| 430 | llmulacc(allocator, rma.limbs, a.limbs, b.limbs); | |
| 610 | 431 | |
| 611 | /// Returns true if a == b. | |
| 612 | pub fn eq(a: Int, b: Int) bool { | |
| 613 | return cmp(a, b) == .eq; | |
| 432 | rma.normalize(a.limbs.len + b.limbs.len); | |
| 433 | rma.positive = (a.positive == b.positive); | |
| 614 | 434 | } |
| 615 | 435 | |
| 616 | // Normalize a possible sequence of leading zeros. | |
| 617 | // | |
| 618 | // [1, 2, 3, 4, 0] -> [1, 2, 3, 4] | |
| 619 | // [1, 2, 0, 0, 0] -> [1, 2] | |
| 620 | // [0, 0, 0, 0, 0] -> [0] | |
| 621 | fn normalize(r: *Int, length: usize) void { | |
| 622 | debug.assert(length > 0); | |
| 623 | debug.assert(length <= r.limbs.len); | |
| 436 | /// q = a / b (rem r) | |
| 437 | /// | |
| 438 | /// a / b are floored (rounded towards 0). | |
| 439 | /// q may alias with a or b. | |
| 440 | /// | |
| 441 | /// Asserts there is enough memory to store q and r. | |
| 442 | /// The upper bound for r limb count is a.limbs.len. | |
| 443 | /// The upper bound for q limb count is given by `a.limbs.len + b.limbs.len + 1`. | |
| 444 | /// | |
| 445 | /// If `allocator` is provided, it will be used for temporary storage to improve | |
| 446 | /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm. | |
| 447 | /// | |
| 448 | /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcDivLimbsBufferLen`. | |
| 449 | pub fn divFloor( | |
| 450 | q: *Mutable, | |
| 451 | r: *Mutable, | |
| 452 | a: Const, | |
| 453 | b: Const, | |
| 454 | limbs_buffer: []Limb, | |
| 455 | allocator: ?*Allocator, | |
| 456 | ) void { | |
| 457 | div(q, r, a, b, limbs_buffer, allocator); | |
| 624 | 458 | |
| 625 | var j = length; | |
| 626 | while (j > 0) : (j -= 1) { | |
| 627 | if (r.limbs[j - 1] != 0) { | |
| 628 | break; | |
| 629 | } | |
| 459 | // Trunc -> Floor. | |
| 460 | if (!q.positive) { | |
| 461 | const one: Const = .{ .limbs = &[_]Limb{1}, .positive = true }; | |
| 462 | q.sub(q.toConst(), one); | |
| 463 | r.add(q.toConst(), one); | |
| 630 | 464 | } |
| 631 | ||
| 632 | // Handle zero | |
| 633 | r.setLen(if (j != 0) j else 1); | |
| 465 | r.positive = b.positive; | |
| 634 | 466 | } |
| 635 | 467 | |
| 636 | // Cannot be used as a result argument to any function. | |
| 637 | fn readOnlyPositive(a: Int) Int { | |
| 638 | return Int{ | |
| 639 | .allocator = null, | |
| 640 | .metadata = a.len(), | |
| 641 | .limbs = a.limbs, | |
| 642 | }; | |
| 468 | /// q = a / b (rem r) | |
| 469 | /// | |
| 470 | /// a / b are truncated (rounded towards -inf). | |
| 471 | /// q may alias with a or b. | |
| 472 | /// | |
| 473 | /// Asserts there is enough memory to store q and r. | |
| 474 | /// The upper bound for r limb count is a.limbs.len. | |
| 475 | /// The upper bound for q limb count is given by `calcQuotientLimbLen`. This accounts | |
| 476 | /// for temporary space used by the division algorithm. | |
| 477 | /// | |
| 478 | /// If `allocator` is provided, it will be used for temporary storage to improve | |
| 479 | /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm. | |
| 480 | /// | |
| 481 | /// `limbs_buffer` is used for temporary storage. The amount required is given by `calcDivLimbsBufferLen`. | |
| 482 | pub fn divTrunc( | |
| 483 | q: *Mutable, | |
| 484 | r: *Mutable, | |
| 485 | a: Const, | |
| 486 | b: Const, | |
| 487 | limbs_buffer: []Limb, | |
| 488 | allocator: ?*Allocator, | |
| 489 | ) void { | |
| 490 | div(q, r, a, b, limbs_buffer, allocator); | |
| 491 | r.positive = a.positive; | |
| 643 | 492 | } |
| 644 | 493 | |
| 645 | /// r = a + b | |
| 494 | /// r = a << shift, in other words, r = a * 2^shift | |
| 646 | 495 | /// |
| 647 | /// r, a and b may be aliases. | |
| 496 | /// r and a may alias. | |
| 648 | 497 | /// |
| 649 | /// Returns an error if memory could not be allocated. | |
| 650 | pub fn add(r: *Int, a: Int, b: Int) Allocator.Error!void { | |
| 651 | r.assertWritable(); | |
| 652 | if (a.eqZero()) { | |
| 653 | try r.copy(b); | |
| 654 | return; | |
| 655 | } else if (b.eqZero()) { | |
| 656 | try r.copy(a); | |
| 657 | return; | |
| 658 | } | |
| 659 | ||
| 660 | if (a.isPositive() != b.isPositive()) { | |
| 661 | if (a.isPositive()) { | |
| 662 | // (a) + (-b) => a - b | |
| 663 | try r.sub(a, readOnlyPositive(b)); | |
| 664 | } else { | |
| 665 | // (-a) + (b) => b - a | |
| 666 | try r.sub(b, readOnlyPositive(a)); | |
| 667 | } | |
| 668 | } else { | |
| 669 | if (a.len() >= b.len()) { | |
| 670 | try r.ensureCapacity(a.len() + 1); | |
| 671 | lladd(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]); | |
| 672 | r.normalize(a.len() + 1); | |
| 673 | } else { | |
| 674 | try r.ensureCapacity(b.len() + 1); | |
| 675 | lladd(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]); | |
| 676 | r.normalize(b.len() + 1); | |
| 677 | } | |
| 678 | ||
| 679 | r.setSign(a.isPositive()); | |
| 680 | } | |
| 498 | /// Asserts there is enough memory to fit the result. The upper bound Limb count is | |
| 499 | /// `a.limbs.len + (shift / (@sizeOf(Limb) * 8))`. | |
| 500 | pub fn shiftLeft(r: *Mutable, a: Const, shift: usize) void { | |
| 501 | llshl(r.limbs[0..], a.limbs[0..a.limbs.len], shift); | |
| 502 | r.normalize(a.limbs.len + (shift / Limb.bit_count) + 1); | |
| 503 | r.positive = a.positive; | |
| 681 | 504 | } |
| 682 | 505 | |
| 683 | // Knuth 4.3.1, Algorithm A. | |
| 684 | fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 685 | @setRuntimeSafety(false); | |
| 686 | debug.assert(a.len != 0 and b.len != 0); | |
| 687 | debug.assert(a.len >= b.len); | |
| 688 | debug.assert(r.len >= a.len + 1); | |
| 689 | ||
| 690 | var i: usize = 0; | |
| 691 | var carry: Limb = 0; | |
| 692 | ||
| 693 | while (i < b.len) : (i += 1) { | |
| 694 | var c: Limb = 0; | |
| 695 | c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i])); | |
| 696 | c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); | |
| 697 | carry = c; | |
| 698 | } | |
| 699 | ||
| 700 | while (i < a.len) : (i += 1) { | |
| 701 | carry = @boolToInt(@addWithOverflow(Limb, a[i], carry, &r[i])); | |
| 506 | /// r = a >> shift | |
| 507 | /// r and a may alias. | |
| 508 | /// | |
| 509 | /// Asserts there is enough memory to fit the result. The upper bound Limb count is | |
| 510 | /// `a.limbs.len - (shift / (@sizeOf(Limb) * 8))`. | |
| 511 | pub fn shiftRight(r: *Mutable, a: Const, shift: usize) void { | |
| 512 | if (a.limbs.len <= shift / Limb.bit_count) { | |
| 513 | r.len = 1; | |
| 514 | r.positive = true; | |
| 515 | r.limbs[0] = 0; | |
| 516 | return; | |
| 702 | 517 | } |
| 703 | 518 | |
| 704 | r[i] = carry; | |
| 519 | const r_len = llshr(r.limbs[0..], a.limbs[0..a.limbs.len], shift); | |
| 520 | r.len = a.limbs.len - (shift / Limb.bit_count); | |
| 521 | r.positive = a.positive; | |
| 705 | 522 | } |
| 706 | 523 | |
| 707 | /// r = a - b | |
| 524 | /// r = a | b | |
| 525 | /// r may alias with a or b. | |
| 708 | 526 | /// |
| 709 | /// r, a and b may be aliases. | |
| 527 | /// a and b are zero-extended to the longer of a or b. | |
| 710 | 528 | /// |
| 711 | /// Returns an error if memory could not be allocated. | |
| 712 | pub fn sub(r: *Int, a: Int, b: Int) !void { | |
| 713 | r.assertWritable(); | |
| 714 | if (a.isPositive() != b.isPositive()) { | |
| 715 | if (a.isPositive()) { | |
| 716 | // (a) - (-b) => a + b | |
| 717 | try r.add(a, readOnlyPositive(b)); | |
| 718 | } else { | |
| 719 | // (-a) - (b) => -(a + b) | |
| 720 | try r.add(readOnlyPositive(a), b); | |
| 721 | r.setSign(false); | |
| 722 | } | |
| 529 | /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`. | |
| 530 | pub fn bitOr(r: *Mutable, a: Const, b: Const) void { | |
| 531 | if (a.limbs.len > b.limbs.len) { | |
| 532 | llor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); | |
| 533 | r.len = a.limbs.len; | |
| 723 | 534 | } else { |
| 724 | if (a.isPositive()) { | |
| 725 | // (a) - (b) => a - b | |
| 726 | if (a.cmp(b) != .lt) { | |
| 727 | try r.ensureCapacity(a.len() + 1); | |
| 728 | llsub(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]); | |
| 729 | r.normalize(a.len()); | |
| 730 | r.setSign(true); | |
| 731 | } else { | |
| 732 | try r.ensureCapacity(b.len() + 1); | |
| 733 | llsub(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]); | |
| 734 | r.normalize(b.len()); | |
| 735 | r.setSign(false); | |
| 736 | } | |
| 737 | } else { | |
| 738 | // (-a) - (-b) => -(a - b) | |
| 739 | if (a.cmp(b) == .lt) { | |
| 740 | try r.ensureCapacity(a.len() + 1); | |
| 741 | llsub(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]); | |
| 742 | r.normalize(a.len()); | |
| 743 | r.setSign(false); | |
| 744 | } else { | |
| 745 | try r.ensureCapacity(b.len() + 1); | |
| 746 | llsub(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]); | |
| 747 | r.normalize(b.len()); | |
| 748 | r.setSign(true); | |
| 749 | } | |
| 750 | } | |
| 535 | llor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); | |
| 536 | r.len = b.limbs.len; | |
| 751 | 537 | } |
| 752 | 538 | } |
| 753 | 539 | |
| 754 | // Knuth 4.3.1, Algorithm S. | |
| 755 | fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 756 | @setRuntimeSafety(false); | |
| 757 | debug.assert(a.len != 0 and b.len != 0); | |
| 758 | debug.assert(a.len > b.len or (a.len == b.len and a[a.len - 1] >= b[b.len - 1])); | |
| 759 | debug.assert(r.len >= a.len); | |
| 760 | ||
| 761 | var i: usize = 0; | |
| 762 | var borrow: Limb = 0; | |
| 763 | ||
| 764 | while (i < b.len) : (i += 1) { | |
| 765 | var c: Limb = 0; | |
| 766 | c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i])); | |
| 767 | c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i])); | |
| 768 | borrow = c; | |
| 540 | /// r = a & b | |
| 541 | /// r may alias with a or b. | |
| 542 | /// | |
| 543 | /// Asserts that r has enough limbs to store the result. Upper bound is `math.min(a.limbs.len, b.limbs.len)`. | |
| 544 | pub fn bitAnd(r: *Mutable, a: Const, b: Const) void { | |
| 545 | if (a.limbs.len > b.limbs.len) { | |
| 546 | lland(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); | |
| 547 | r.normalize(b.limbs.len); | |
| 548 | } else { | |
| 549 | lland(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); | |
| 550 | r.normalize(a.limbs.len); | |
| 769 | 551 | } |
| 552 | } | |
| 770 | 553 | |
| 771 | while (i < a.len) : (i += 1) { | |
| 772 | borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i])); | |
| 554 | /// r = a ^ b | |
| 555 | /// r may alias with a or b. | |
| 556 | /// | |
| 557 | /// Asserts that r has enough limbs to store the result. Upper bound is `math.max(a.limbs.len, b.limbs.len)`. | |
| 558 | pub fn bitXor(r: *Mutable, a: Const, b: Const) void { | |
| 559 | if (a.limbs.len > b.limbs.len) { | |
| 560 | llxor(r.limbs[0..], a.limbs[0..a.limbs.len], b.limbs[0..b.limbs.len]); | |
| 561 | r.normalize(a.limbs.len); | |
| 562 | } else { | |
| 563 | llxor(r.limbs[0..], b.limbs[0..b.limbs.len], a.limbs[0..a.limbs.len]); | |
| 564 | r.normalize(b.limbs.len); | |
| 773 | 565 | } |
| 774 | ||
| 775 | debug.assert(borrow == 0); | |
| 776 | 566 | } |
| 777 | 567 | |
| 778 | /// rma = a * b | |
| 568 | /// rma may alias x or y. | |
| 569 | /// x and y may alias each other. | |
| 570 | /// Asserts that `rma` has enough limbs to store the result. Upper bound is | |
| 571 | /// `math.min(x.limbs.len, y.limbs.len)`. | |
| 779 | 572 | /// |
| 780 | /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. | |
| 573 | /// `limbs_buffer` is used for temporary storage during the operation. When this function returns, | |
| 574 | /// it will have the same length as it had when the function was called. | |
| 575 | pub fn gcd(rma: *Mutable, x: Const, y: Const, limbs_buffer: *std.ArrayList(Limb)) !void { | |
| 576 | const prev_len = limbs_buffer.items.len; | |
| 577 | defer limbs_buffer.shrink(prev_len); | |
| 578 | const x_copy = if (rma.limbs.ptr == x.limbs.ptr) blk: { | |
| 579 | const start = limbs_buffer.items.len; | |
| 580 | try limbs_buffer.appendSlice(x.limbs); | |
| 581 | break :blk x.toMutable(limbs_buffer.items[start..]).toConst(); | |
| 582 | } else x; | |
| 583 | const y_copy = if (rma.limbs.ptr == y.limbs.ptr) blk: { | |
| 584 | const start = limbs_buffer.items.len; | |
| 585 | try limbs_buffer.appendSlice(y.limbs); | |
| 586 | break :blk y.toMutable(limbs_buffer.items[start..]).toConst(); | |
| 587 | } else y; | |
| 588 | ||
| 589 | return gcdLehmer(rma, x_copy, y_copy, limbs_buffer); | |
| 590 | } | |
| 591 | ||
| 592 | /// rma may not alias x or y. | |
| 593 | /// x and y may alias each other. | |
| 594 | /// Asserts that `rma` has enough limbs to store the result. Upper bound is given by `calcGcdNoAliasLimbLen`. | |
| 781 | 595 | /// |
| 782 | /// Returns an error if memory could not be allocated. | |
| 783 | pub fn mul(rma: *Int, a: Int, b: Int) !void { | |
| 784 | rma.assertWritable(); | |
| 596 | /// `limbs_buffer` is used for temporary storage during the operation. | |
| 597 | pub fn gcdNoAlias(rma: *Mutable, x: Const, y: Const, limbs_buffer: *std.ArrayList(Limb)) !void { | |
| 598 | assert(rma.limbs.ptr != x.limbs.ptr); // illegal aliasing | |
| 599 | assert(rma.limbs.ptr != y.limbs.ptr); // illegal aliasing | |
| 600 | return gcdLehmer(rma, x, y, allocator); | |
| 601 | } | |
| 785 | 602 | |
| 786 | var r = rma; | |
| 787 | var aliased = rma.limbs.ptr == a.limbs.ptr or rma.limbs.ptr == b.limbs.ptr; | |
| 603 | fn gcdLehmer(result: *Mutable, xa: Const, ya: Const, limbs_buffer: *std.ArrayList(Limb)) !void { | |
| 604 | var x = try xa.toManaged(limbs_buffer.allocator); | |
| 605 | defer x.deinit(); | |
| 606 | x.abs(); | |
| 607 | ||
| 608 | var y = try ya.toManaged(limbs_buffer.allocator); | |
| 609 | defer y.deinit(); | |
| 610 | y.abs(); | |
| 788 | 611 | |
| 789 | var sr: Int = undefined; | |
| 790 | if (aliased) { | |
| 791 | sr = try Int.initCapacity(rma.allocator.?, a.len() + b.len()); | |
| 792 | r = &sr; | |
| 793 | aliased = true; | |
| 612 | if (x.toConst().order(y.toConst()) == .lt) { | |
| 613 | x.swap(&y); | |
| 794 | 614 | } |
| 795 | defer if (aliased) { | |
| 796 | rma.swap(r); | |
| 797 | r.deinit(); | |
| 798 | }; | |
| 799 | 615 | |
| 800 | try r.ensureCapacity(a.len() + b.len() + 1); | |
| 616 | var t_big = try Managed.init(limbs_buffer.allocator); | |
| 617 | defer t_big.deinit(); | |
| 801 | 618 | |
| 802 | mem.set(Limb, r.limbs[0 .. a.len() + b.len() + 1], 0); | |
| 619 | var r = try Managed.init(limbs_buffer.allocator); | |
| 620 | defer r.deinit(); | |
| 803 | 621 | |
| 804 | try llmulacc(rma.allocator.?, r.limbs, a.limbs[0..a.len()], b.limbs[0..b.len()]); | |
| 622 | while (y.len() > 1) { | |
| 623 | assert(x.isPositive() and y.isPositive()); | |
| 624 | assert(x.len() >= y.len()); | |
| 805 | 625 | |
| 806 | r.normalize(a.len() + b.len()); | |
| 807 | r.setSign(a.isPositive() == b.isPositive()); | |
| 808 | } | |
| 626 | var xh: SignedDoubleLimb = x.limbs[x.len() - 1]; | |
| 627 | var yh: SignedDoubleLimb = if (x.len() > y.len()) 0 else y.limbs[x.len() - 1]; | |
| 809 | 628 | |
| 810 | // a + b * c + *carry, sets carry to the overflow bits | |
| 811 | pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { | |
| 812 | @setRuntimeSafety(false); | |
| 813 | var r1: Limb = undefined; | |
| 629 | var A: SignedDoubleLimb = 1; | |
| 630 | var B: SignedDoubleLimb = 0; | |
| 631 | var C: SignedDoubleLimb = 0; | |
| 632 | var D: SignedDoubleLimb = 1; | |
| 814 | 633 | |
| 815 | // r1 = a + *carry | |
| 816 | const c1: Limb = @boolToInt(@addWithOverflow(Limb, a, carry.*, &r1)); | |
| 634 | while (yh + C != 0 and yh + D != 0) { | |
| 635 | const q = @divFloor(xh + A, yh + C); | |
| 636 | const qp = @divFloor(xh + B, yh + D); | |
| 637 | if (q != qp) { | |
| 638 | break; | |
| 639 | } | |
| 817 | 640 | |
| 818 | // r2 = b * c | |
| 819 | const bc = @as(DoubleLimb, math.mulWide(Limb, b, c)); | |
| 820 | const r2 = @truncate(Limb, bc); | |
| 821 | const c2 = @truncate(Limb, bc >> Limb.bit_count); | |
| 641 | var t = A - q * C; | |
| 642 | A = C; | |
| 643 | C = t; | |
| 644 | t = B - q * D; | |
| 645 | B = D; | |
| 646 | D = t; | |
| 822 | 647 | |
| 823 | // r1 = r1 + r2 | |
| 824 | const c3: Limb = @boolToInt(@addWithOverflow(Limb, r1, r2, &r1)); | |
| 648 | t = xh - q * yh; | |
| 649 | xh = yh; | |
| 650 | yh = t; | |
| 651 | } | |
| 825 | 652 | |
| 826 | // This never overflows, c1, c3 are either 0 or 1 and if both are 1 then | |
| 827 | // c2 is at least <= maxInt(Limb) - 2. | |
| 828 | carry.* = c1 + c2 + c3; | |
| 653 | if (B == 0) { | |
| 654 | // t_big = x % y, r is unused | |
| 655 | try r.divTrunc(&t_big, x.toConst(), y.toConst()); | |
| 656 | assert(t_big.isPositive()); | |
| 829 | 657 | |
| 830 | return r1; | |
| 831 | } | |
| 658 | x.swap(&y); | |
| 659 | y.swap(&t_big); | |
| 660 | } else { | |
| 661 | var storage: [8]Limb = undefined; | |
| 662 | const Ap = fixedIntFromSignedDoubleLimb(A, storage[0..2]).toConst(); | |
| 663 | const Bp = fixedIntFromSignedDoubleLimb(B, storage[2..4]).toConst(); | |
| 664 | const Cp = fixedIntFromSignedDoubleLimb(C, storage[4..6]).toConst(); | |
| 665 | const Dp = fixedIntFromSignedDoubleLimb(D, storage[6..8]).toConst(); | |
| 832 | 666 | |
| 833 | fn llmulDigit(acc: []Limb, y: []const Limb, xi: Limb) void { | |
| 834 | @setRuntimeSafety(false); | |
| 835 | if (xi == 0) { | |
| 836 | return; | |
| 837 | } | |
| 667 | // t_big = Ax + By | |
| 668 | try r.mul(x.toConst(), Ap); | |
| 669 | try t_big.mul(y.toConst(), Bp); | |
| 670 | try t_big.add(r.toConst(), t_big.toConst()); | |
| 838 | 671 | |
| 839 | var carry: usize = 0; | |
| 840 | var a_lo = acc[0..y.len]; | |
| 841 | var a_hi = acc[y.len..]; | |
| 672 | // u = Cx + Dy, r as u | |
| 673 | try x.mul(x.toConst(), Cp); | |
| 674 | try r.mul(y.toConst(), Dp); | |
| 675 | try r.add(x.toConst(), r.toConst()); | |
| 842 | 676 | |
| 843 | var j: usize = 0; | |
| 844 | while (j < a_lo.len) : (j += 1) { | |
| 845 | a_lo[j] = @call(.{ .modifier = .always_inline }, addMulLimbWithCarry, .{ a_lo[j], y[j], xi, &carry }); | |
| 677 | x.swap(&t_big); | |
| 678 | y.swap(&r); | |
| 679 | } | |
| 846 | 680 | } |
| 847 | 681 | |
| 848 | j = 0; | |
| 849 | while ((carry != 0) and (j < a_hi.len)) : (j += 1) { | |
| 850 | carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j])); | |
| 682 | // euclidean algorithm | |
| 683 | assert(x.toConst().order(y.toConst()) != .lt); | |
| 684 | ||
| 685 | while (!y.toConst().eqZero()) { | |
| 686 | try t_big.divTrunc(&r, x.toConst(), y.toConst()); | |
| 687 | x.swap(&y); | |
| 688 | y.swap(&r); | |
| 851 | 689 | } |
| 690 | ||
| 691 | result.copy(x.toConst()); | |
| 852 | 692 | } |
| 853 | 693 | |
| 854 | // Knuth 4.3.1, Algorithm M. | |
| 855 | // | |
| 856 | // r MUST NOT alias any of a or b. | |
| 857 | fn llmulacc(allocator: *Allocator, r: []Limb, a: []const Limb, b: []const Limb) error{OutOfMemory}!void { | |
| 858 | @setRuntimeSafety(false); | |
| 694 | /// Truncates by default. | |
| 695 | fn div(quo: *Mutable, rem: *Mutable, a: Const, b: Const, limbs_buffer: []Limb, allocator: ?*Allocator) void { | |
| 696 | assert(!b.eqZero()); // division by zero | |
| 697 | assert(quo != rem); // illegal aliasing | |
| 859 | 698 | |
| 860 | const a_norm = a[0..llnormalize(a)]; | |
| 861 | const b_norm = b[0..llnormalize(b)]; | |
| 862 | var x = a_norm; | |
| 863 | var y = b_norm; | |
| 864 | if (a_norm.len > b_norm.len) { | |
| 865 | x = b_norm; | |
| 866 | y = a_norm; | |
| 867 | } | |
| 699 | if (a.orderAbs(b) == .lt) { | |
| 700 | // quo may alias a so handle rem first | |
| 701 | rem.copy(a); | |
| 702 | rem.positive = a.positive == b.positive; | |
| 868 | 703 | |
| 869 | debug.assert(r.len >= x.len + y.len + 1); | |
| 704 | quo.positive = true; | |
| 705 | quo.len = 1; | |
| 706 | quo.limbs[0] = 0; | |
| 707 | return; | |
| 708 | } | |
| 870 | 709 | |
| 871 | // 48 is a pretty abitrary size chosen based on performance of a factorial program. | |
| 872 | if (x.len <= 48) { | |
| 873 | // Basecase multiplication | |
| 710 | // Handle trailing zero-words of divisor/dividend. These are not handled in the following | |
| 711 | // algorithms. | |
| 712 | const a_zero_limb_count = blk: { | |
| 874 | 713 | var i: usize = 0; |
| 875 | while (i < x.len) : (i += 1) { | |
| 876 | llmulDigit(r[i..], y, x[i]); | |
| 714 | while (i < a.limbs.len) : (i += 1) { | |
| 715 | if (a.limbs[i] != 0) break; | |
| 877 | 716 | } |
| 878 | } else { | |
| 879 | // Karatsuba multiplication | |
| 880 | const split = @divFloor(x.len, 2); | |
| 881 | var x0 = x[0..split]; | |
| 882 | var x1 = x[split..x.len]; | |
| 883 | var y0 = y[0..split]; | |
| 884 | var y1 = y[split..y.len]; | |
| 885 | ||
| 886 | var tmp = try allocator.alloc(Limb, x1.len + y1.len + 1); | |
| 887 | defer allocator.free(tmp); | |
| 888 | mem.set(Limb, tmp, 0); | |
| 889 | ||
| 890 | try llmulacc(allocator, tmp, x1, y1); | |
| 717 | break :blk i; | |
| 718 | }; | |
| 719 | const b_zero_limb_count = blk: { | |
| 720 | var i: usize = 0; | |
| 721 | while (i < b.limbs.len) : (i += 1) { | |
| 722 | if (b.limbs[i] != 0) break; | |
| 723 | } | |
| 724 | break :blk i; | |
| 725 | }; | |
| 891 | 726 | |
| 892 | var length = llnormalize(tmp); | |
| 893 | _ = llaccum(r[split..], tmp[0..length]); | |
| 894 | _ = llaccum(r[split * 2 ..], tmp[0..length]); | |
| 727 | const ab_zero_limb_count = math.min(a_zero_limb_count, b_zero_limb_count); | |
| 895 | 728 | |
| 896 | mem.set(Limb, tmp[0..length], 0); | |
| 729 | if (b.limbs.len - ab_zero_limb_count == 1) { | |
| 730 | lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[ab_zero_limb_count..a.limbs.len], b.limbs[b.limbs.len - 1]); | |
| 731 | quo.normalize(a.limbs.len - ab_zero_limb_count); | |
| 732 | quo.positive = (a.positive == b.positive); | |
| 897 | 733 | |
| 898 | try llmulacc(allocator, tmp, x0, y0); | |
| 734 | rem.len = 1; | |
| 735 | rem.positive = true; | |
| 736 | } else { | |
| 737 | // x and y are modified during division | |
| 738 | const sep_len = calcMulLimbsBufferLen(a.limbs.len, b.limbs.len, 2); | |
| 739 | const x_limbs = limbs_buffer[0 * sep_len ..][0..sep_len]; | |
| 740 | const y_limbs = limbs_buffer[1 * sep_len ..][0..sep_len]; | |
| 741 | const t_limbs = limbs_buffer[2 * sep_len ..][0..sep_len]; | |
| 742 | const mul_limbs_buf = limbs_buffer[3 * sep_len ..][0..sep_len]; | |
| 743 | ||
| 744 | var x: Mutable = .{ | |
| 745 | .limbs = x_limbs, | |
| 746 | .positive = a.positive, | |
| 747 | .len = a.limbs.len - ab_zero_limb_count, | |
| 748 | }; | |
| 749 | var y: Mutable = .{ | |
| 750 | .limbs = y_limbs, | |
| 751 | .positive = b.positive, | |
| 752 | .len = b.limbs.len - ab_zero_limb_count, | |
| 753 | }; | |
| 899 | 754 | |
| 900 | length = llnormalize(tmp); | |
| 901 | _ = llaccum(r[0..], tmp[0..length]); | |
| 902 | _ = llaccum(r[split..], tmp[0..length]); | |
| 755 | // Shrink x, y such that the trailing zero limbs shared between are removed. | |
| 756 | mem.copy(Limb, x.limbs, a.limbs[ab_zero_limb_count..a.limbs.len]); | |
| 757 | mem.copy(Limb, y.limbs, b.limbs[ab_zero_limb_count..b.limbs.len]); | |
| 903 | 758 | |
| 904 | const x_cmp = llcmp(x1, x0); | |
| 905 | const y_cmp = llcmp(y1, y0); | |
| 906 | if (x_cmp * y_cmp == 0) { | |
| 907 | return; | |
| 908 | } | |
| 909 | const x0_len = llnormalize(x0); | |
| 910 | const x1_len = llnormalize(x1); | |
| 911 | var j0 = try allocator.alloc(Limb, math.max(x0_len, x1_len)); | |
| 912 | defer allocator.free(j0); | |
| 913 | if (x_cmp == 1) { | |
| 914 | llsub(j0, x1[0..x1_len], x0[0..x0_len]); | |
| 915 | } else { | |
| 916 | llsub(j0, x0[0..x0_len], x1[0..x1_len]); | |
| 917 | } | |
| 759 | divN(quo, rem, &x, &y, t_limbs, mul_limbs_buf, allocator); | |
| 760 | quo.positive = (a.positive == b.positive); | |
| 761 | } | |
| 918 | 762 | |
| 919 | const y0_len = llnormalize(y0); | |
| 920 | const y1_len = llnormalize(y1); | |
| 921 | var j1 = try allocator.alloc(Limb, math.max(y0_len, y1_len)); | |
| 922 | defer allocator.free(j1); | |
| 923 | if (y_cmp == 1) { | |
| 924 | llsub(j1, y1[0..y1_len], y0[0..y0_len]); | |
| 925 | } else { | |
| 926 | llsub(j1, y0[0..y0_len], y1[0..y1_len]); | |
| 927 | } | |
| 928 | const j0_len = llnormalize(j0); | |
| 929 | const j1_len = llnormalize(j1); | |
| 930 | if (x_cmp == y_cmp) { | |
| 931 | mem.set(Limb, tmp[0..length], 0); | |
| 932 | try llmulacc(allocator, tmp, j0, j1); | |
| 933 | ||
| 934 | length = Int.llnormalize(tmp); | |
| 935 | llsub(r[split..], r[split..], tmp[0..length]); | |
| 936 | } else { | |
| 937 | try llmulacc(allocator, r[split..], j0, j1); | |
| 938 | } | |
| 763 | if (ab_zero_limb_count != 0) { | |
| 764 | rem.shiftLeft(rem.toConst(), ab_zero_limb_count * Limb.bit_count); | |
| 939 | 765 | } |
| 940 | 766 | } |
| 941 | 767 | |
| 942 | // r = r + a | |
| 943 | fn llaccum(r: []Limb, a: []const Limb) Limb { | |
| 944 | @setRuntimeSafety(false); | |
| 945 | debug.assert(r.len != 0 and a.len != 0); | |
| 946 | debug.assert(r.len >= a.len); | |
| 947 | ||
| 948 | var i: usize = 0; | |
| 949 | var carry: Limb = 0; | |
| 950 | ||
| 951 | while (i < a.len) : (i += 1) { | |
| 952 | var c: Limb = 0; | |
| 953 | c += @boolToInt(@addWithOverflow(Limb, r[i], a[i], &r[i])); | |
| 954 | c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); | |
| 955 | carry = c; | |
| 956 | } | |
| 768 | /// Handbook of Applied Cryptography, 14.20 | |
| 769 | /// | |
| 770 | /// x = qy + r where 0 <= r < y | |
| 771 | fn divN( | |
| 772 | q: *Mutable, | |
| 773 | r: *Mutable, | |
| 774 | x: *Mutable, | |
| 775 | y: *Mutable, | |
| 776 | tmp_limbs: []Limb, | |
| 777 | mul_limb_buf: []Limb, | |
| 778 | allocator: ?*Allocator, | |
| 779 | ) void { | |
| 780 | assert(y.len >= 2); | |
| 781 | assert(x.len >= y.len); | |
| 782 | assert(q.limbs.len >= x.len + y.len - 1); | |
| 783 | ||
| 784 | // See 3.2 | |
| 785 | var backup_tmp_limbs: [3]Limb = undefined; | |
| 786 | const t_limbs = if (tmp_limbs.len < 3) &backup_tmp_limbs else tmp_limbs; | |
| 787 | ||
| 788 | var tmp: Mutable = .{ | |
| 789 | .limbs = t_limbs, | |
| 790 | .len = 1, | |
| 791 | .positive = true, | |
| 792 | }; | |
| 793 | tmp.limbs[0] = 0; | |
| 957 | 794 | |
| 958 | while ((carry != 0) and i < r.len) : (i += 1) { | |
| 959 | carry = @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); | |
| 795 | // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even | |
| 796 | var norm_shift = @clz(Limb, y.limbs[y.len - 1]); | |
| 797 | if (norm_shift == 0 and y.toConst().isOdd()) { | |
| 798 | norm_shift = Limb.bit_count; | |
| 960 | 799 | } |
| 800 | x.shiftLeft(x.toConst(), norm_shift); | |
| 801 | y.shiftLeft(y.toConst(), norm_shift); | |
| 961 | 802 | |
| 962 | return carry; | |
| 963 | } | |
| 803 | const n = x.len - 1; | |
| 804 | const t = y.len - 1; | |
| 964 | 805 | |
| 965 | /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively for limbs. | |
| 966 | pub fn llcmp(a: []const Limb, b: []const Limb) i8 { | |
| 967 | @setRuntimeSafety(false); | |
| 968 | const a_len = llnormalize(a); | |
| 969 | const b_len = llnormalize(b); | |
| 970 | if (a_len < b_len) { | |
| 971 | return -1; | |
| 972 | } | |
| 973 | if (a_len > b_len) { | |
| 974 | return 1; | |
| 975 | } | |
| 806 | // 1. | |
| 807 | q.len = n - t + 1; | |
| 808 | q.positive = true; | |
| 809 | mem.set(Limb, q.limbs[0..q.len], 0); | |
| 976 | 810 | |
| 977 | var i: usize = a_len - 1; | |
| 978 | while (i != 0) : (i -= 1) { | |
| 979 | if (a[i] != b[i]) { | |
| 980 | break; | |
| 981 | } | |
| 982 | } | |
| 983 | ||
| 984 | if (a[i] < b[i]) { | |
| 985 | return -1; | |
| 986 | } else if (a[i] > b[i]) { | |
| 987 | return 1; | |
| 988 | } else { | |
| 989 | return 0; | |
| 990 | } | |
| 991 | } | |
| 992 | ||
| 993 | // returns the min length the limb could be. | |
| 994 | fn llnormalize(a: []const Limb) usize { | |
| 995 | @setRuntimeSafety(false); | |
| 996 | var j = a.len; | |
| 997 | while (j > 0) : (j -= 1) { | |
| 998 | if (a[j - 1] != 0) { | |
| 999 | break; | |
| 1000 | } | |
| 1001 | } | |
| 1002 | ||
| 1003 | // Handle zero | |
| 1004 | return if (j != 0) j else 1; | |
| 1005 | } | |
| 1006 | ||
| 1007 | /// q = a / b (rem r) | |
| 1008 | /// | |
| 1009 | /// a / b are floored (rounded towards 0). | |
| 1010 | pub fn divFloor(q: *Int, r: *Int, a: Int, b: Int) !void { | |
| 1011 | try div(q, r, a, b); | |
| 1012 | ||
| 1013 | // Trunc -> Floor. | |
| 1014 | if (!q.isPositive()) { | |
| 1015 | const one = Int.initFixed(([_]Limb{1})[0..]); | |
| 1016 | try q.sub(q.*, one); | |
| 1017 | try r.add(q.*, one); | |
| 1018 | } | |
| 1019 | r.setSign(b.isPositive()); | |
| 1020 | } | |
| 1021 | ||
| 1022 | /// q = a / b (rem r) | |
| 1023 | /// | |
| 1024 | /// a / b are truncated (rounded towards -inf). | |
| 1025 | pub fn divTrunc(q: *Int, r: *Int, a: Int, b: Int) !void { | |
| 1026 | try div(q, r, a, b); | |
| 1027 | r.setSign(a.isPositive()); | |
| 1028 | } | |
| 1029 | ||
| 1030 | // Truncates by default. | |
| 1031 | fn div(quo: *Int, rem: *Int, a: Int, b: Int) !void { | |
| 1032 | quo.assertWritable(); | |
| 1033 | rem.assertWritable(); | |
| 1034 | ||
| 1035 | if (b.eqZero()) { | |
| 1036 | @panic("division by zero"); | |
| 1037 | } | |
| 1038 | if (quo == rem) { | |
| 1039 | @panic("quo and rem cannot be same variable"); | |
| 1040 | } | |
| 1041 | ||
| 1042 | if (a.cmpAbs(b) == .lt) { | |
| 1043 | // quo may alias a so handle rem first | |
| 1044 | try rem.copy(a); | |
| 1045 | rem.setSign(a.isPositive() == b.isPositive()); | |
| 1046 | ||
| 1047 | quo.metadata = 1; | |
| 1048 | quo.limbs[0] = 0; | |
| 1049 | return; | |
| 1050 | } | |
| 1051 | ||
| 1052 | // Handle trailing zero-words of divisor/dividend. These are not handled in the following | |
| 1053 | // algorithms. | |
| 1054 | const a_zero_limb_count = blk: { | |
| 1055 | var i: usize = 0; | |
| 1056 | while (i < a.len()) : (i += 1) { | |
| 1057 | if (a.limbs[i] != 0) break; | |
| 1058 | } | |
| 1059 | break :blk i; | |
| 1060 | }; | |
| 1061 | const b_zero_limb_count = blk: { | |
| 1062 | var i: usize = 0; | |
| 1063 | while (i < b.len()) : (i += 1) { | |
| 1064 | if (b.limbs[i] != 0) break; | |
| 1065 | } | |
| 1066 | break :blk i; | |
| 1067 | }; | |
| 1068 | ||
| 1069 | const ab_zero_limb_count = std.math.min(a_zero_limb_count, b_zero_limb_count); | |
| 1070 | ||
| 1071 | if (b.len() - ab_zero_limb_count == 1) { | |
| 1072 | try quo.ensureCapacity(a.len()); | |
| 1073 | ||
| 1074 | lldiv1(quo.limbs[0..], &rem.limbs[0], a.limbs[ab_zero_limb_count..a.len()], b.limbs[b.len() - 1]); | |
| 1075 | quo.normalize(a.len() - ab_zero_limb_count); | |
| 1076 | quo.setSign(a.isPositive() == b.isPositive()); | |
| 1077 | ||
| 1078 | rem.metadata = 1; | |
| 1079 | } else { | |
| 1080 | // x and y are modified during division | |
| 1081 | var x = try Int.initCapacity(quo.allocator.?, a.len()); | |
| 1082 | defer x.deinit(); | |
| 1083 | try x.copy(a); | |
| 1084 | ||
| 1085 | var y = try Int.initCapacity(quo.allocator.?, b.len()); | |
| 1086 | defer y.deinit(); | |
| 1087 | try y.copy(b); | |
| 1088 | ||
| 1089 | // x may grow one limb during normalization | |
| 1090 | try quo.ensureCapacity(a.len() + y.len()); | |
| 1091 | ||
| 1092 | // Shrink x, y such that the trailing zero limbs shared between are removed. | |
| 1093 | if (ab_zero_limb_count != 0) { | |
| 1094 | std.mem.copy(Limb, x.limbs[0..], x.limbs[ab_zero_limb_count..]); | |
| 1095 | std.mem.copy(Limb, y.limbs[0..], y.limbs[ab_zero_limb_count..]); | |
| 1096 | x.metadata -= ab_zero_limb_count; | |
| 1097 | y.metadata -= ab_zero_limb_count; | |
| 1098 | } | |
| 1099 | ||
| 1100 | try divN(quo.allocator.?, quo, rem, &x, &y); | |
| 1101 | quo.setSign(a.isPositive() == b.isPositive()); | |
| 1102 | } | |
| 1103 | ||
| 1104 | if (ab_zero_limb_count != 0) { | |
| 1105 | try rem.shiftLeft(rem.*, ab_zero_limb_count * Limb.bit_count); | |
| 1106 | } | |
| 1107 | } | |
| 1108 | ||
| 1109 | // Knuth 4.3.1, Exercise 16. | |
| 1110 | fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void { | |
| 1111 | @setRuntimeSafety(false); | |
| 1112 | debug.assert(a.len > 1 or a[0] >= b); | |
| 1113 | debug.assert(quo.len >= a.len); | |
| 1114 | ||
| 1115 | rem.* = 0; | |
| 1116 | for (a) |_, ri| { | |
| 1117 | const i = a.len - ri - 1; | |
| 1118 | const pdiv = ((@as(DoubleLimb, rem.*) << Limb.bit_count) | a[i]); | |
| 1119 | ||
| 1120 | if (pdiv == 0) { | |
| 1121 | quo[i] = 0; | |
| 1122 | rem.* = 0; | |
| 1123 | } else if (pdiv < b) { | |
| 1124 | quo[i] = 0; | |
| 1125 | rem.* = @truncate(Limb, pdiv); | |
| 1126 | } else if (pdiv == b) { | |
| 1127 | quo[i] = 1; | |
| 1128 | rem.* = 0; | |
| 1129 | } else { | |
| 1130 | quo[i] = @truncate(Limb, @divTrunc(pdiv, b)); | |
| 1131 | rem.* = @truncate(Limb, pdiv - (quo[i] *% b)); | |
| 1132 | } | |
| 1133 | } | |
| 1134 | } | |
| 1135 | ||
| 1136 | // Handbook of Applied Cryptography, 14.20 | |
| 1137 | // | |
| 1138 | // x = qy + r where 0 <= r < y | |
| 1139 | fn divN(allocator: *Allocator, q: *Int, r: *Int, x: *Int, y: *Int) !void { | |
| 1140 | debug.assert(y.len() >= 2); | |
| 1141 | debug.assert(x.len() >= y.len()); | |
| 1142 | debug.assert(q.limbs.len >= x.len() + y.len() - 1); | |
| 1143 | debug.assert(default_capacity >= 3); // see 3.2 | |
| 1144 | ||
| 1145 | var tmp = try Int.init(allocator); | |
| 1146 | defer tmp.deinit(); | |
| 1147 | ||
| 1148 | // Normalize so y > Limb.bit_count / 2 (i.e. leading bit is set) and even | |
| 1149 | var norm_shift = @clz(Limb, y.limbs[y.len() - 1]); | |
| 1150 | if (norm_shift == 0 and y.isOdd()) { | |
| 1151 | norm_shift = Limb.bit_count; | |
| 1152 | } | |
| 1153 | try x.shiftLeft(x.*, norm_shift); | |
| 1154 | try y.shiftLeft(y.*, norm_shift); | |
| 1155 | ||
| 1156 | const n = x.len() - 1; | |
| 1157 | const t = y.len() - 1; | |
| 1158 | ||
| 1159 | // 1. | |
| 1160 | q.metadata = n - t + 1; | |
| 1161 | mem.set(Limb, q.limbs[0..q.len()], 0); | |
| 1162 | ||
| 1163 | // 2. | |
| 1164 | try tmp.shiftLeft(y.*, Limb.bit_count * (n - t)); | |
| 1165 | while (x.cmp(tmp) != .lt) { | |
| 1166 | q.limbs[n - t] += 1; | |
| 1167 | try x.sub(x.*, tmp); | |
| 811 | // 2. | |
| 812 | tmp.shiftLeft(y.toConst(), Limb.bit_count * (n - t)); | |
| 813 | while (x.toConst().order(tmp.toConst()) != .lt) { | |
| 814 | q.limbs[n - t] += 1; | |
| 815 | x.sub(x.toConst(), tmp.toConst()); | |
| 1168 | 816 | } |
| 1169 | 817 | |
| 1170 | 818 | // 3. |
| ... | ... | @@ -1193,7 +841,7 @@ pub const Int = struct { |
| 1193 | 841 | r.limbs[2] = carry; |
| 1194 | 842 | r.normalize(3); |
| 1195 | 843 | |
| 1196 | if (r.cmpAbs(tmp) != .gt) { | |
| 844 | if (r.toConst().orderAbs(tmp.toConst()) != .gt) { | |
| 1197 | 845 | break; |
| 1198 | 846 | } |
| 1199 | 847 | |
| ... | ... | @@ -1201,1748 +849,1284 @@ pub const Int = struct { |
| 1201 | 849 | } |
| 1202 | 850 | |
| 1203 | 851 | // 3.3 |
| 1204 | try tmp.set(q.limbs[i - t - 1]); | |
| 1205 | try tmp.mul(tmp, y.*); | |
| 1206 | try tmp.shiftLeft(tmp, Limb.bit_count * (i - t - 1)); | |
| 1207 | try x.sub(x.*, tmp); | |
| 1208 | ||
| 1209 | if (!x.isPositive()) { | |
| 1210 | try tmp.shiftLeft(y.*, Limb.bit_count * (i - t - 1)); | |
| 1211 | try x.add(x.*, tmp); | |
| 852 | tmp.set(q.limbs[i - t - 1]); | |
| 853 | tmp.mul(tmp.toConst(), y.toConst(), mul_limb_buf, allocator); | |
| 854 | tmp.shiftLeft(tmp.toConst(), Limb.bit_count * (i - t - 1)); | |
| 855 | x.sub(x.toConst(), tmp.toConst()); | |
| 856 | ||
| 857 | if (!x.positive) { | |
| 858 | tmp.shiftLeft(y.toConst(), Limb.bit_count * (i - t - 1)); | |
| 859 | x.add(x.toConst(), tmp.toConst()); | |
| 1212 | 860 | q.limbs[i - t - 1] -= 1; |
| 1213 | 861 | } |
| 1214 | 862 | } |
| 1215 | 863 | |
| 1216 | 864 | // Denormalize |
| 1217 | q.normalize(q.len()); | |
| 865 | q.normalize(q.len); | |
| 1218 | 866 | |
| 1219 | try r.shiftRight(x.*, norm_shift); | |
| 1220 | r.normalize(r.len()); | |
| 867 | r.shiftRight(x.toConst(), norm_shift); | |
| 868 | r.normalize(r.len); | |
| 1221 | 869 | } |
| 1222 | 870 | |
| 1223 | /// r = a << shift, in other words, r = a * 2^shift | |
| 1224 | pub fn shiftLeft(r: *Int, a: Int, shift: usize) !void { | |
| 1225 | r.assertWritable(); | |
| 1226 | ||
| 1227 | try r.ensureCapacity(a.len() + (shift / Limb.bit_count) + 1); | |
| 1228 | llshl(r.limbs[0..], a.limbs[0..a.len()], shift); | |
| 1229 | r.normalize(a.len() + (shift / Limb.bit_count) + 1); | |
| 1230 | r.setSign(a.isPositive()); | |
| 871 | /// Normalize a possible sequence of leading zeros. | |
| 872 | /// | |
| 873 | /// [1, 2, 3, 4, 0] -> [1, 2, 3, 4] | |
| 874 | /// [1, 2, 0, 0, 0] -> [1, 2] | |
| 875 | /// [0, 0, 0, 0, 0] -> [0] | |
| 876 | fn normalize(r: *Mutable, length: usize) void { | |
| 877 | r.len = llnormalize(r.limbs[0..length]); | |
| 1231 | 878 | } |
| 879 | }; | |
| 1232 | 880 | |
| 1233 | fn llshl(r: []Limb, a: []const Limb, shift: usize) void { | |
| 1234 | @setRuntimeSafety(false); | |
| 1235 | debug.assert(a.len >= 1); | |
| 1236 | debug.assert(r.len >= a.len + (shift / Limb.bit_count) + 1); | |
| 1237 | ||
| 1238 | const limb_shift = shift / Limb.bit_count + 1; | |
| 1239 | const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count); | |
| 1240 | ||
| 1241 | var carry: Limb = 0; | |
| 1242 | var i: usize = 0; | |
| 1243 | while (i < a.len) : (i += 1) { | |
| 1244 | const src_i = a.len - i - 1; | |
| 1245 | const dst_i = src_i + limb_shift; | |
| 1246 | ||
| 1247 | const src_digit = a[src_i]; | |
| 1248 | r[dst_i] = carry | @call(.{ .modifier = .always_inline }, math.shr, .{ | |
| 1249 | Limb, | |
| 1250 | src_digit, | |
| 1251 | Limb.bit_count - @intCast(Limb, interior_limb_shift), | |
| 1252 | }); | |
| 1253 | carry = (src_digit << interior_limb_shift); | |
| 1254 | } | |
| 1255 | ||
| 1256 | r[limb_shift - 1] = carry; | |
| 1257 | mem.set(Limb, r[0 .. limb_shift - 1], 0); | |
| 881 | /// A arbitrary-precision big integer, with a fixed set of immutable limbs. | |
| 882 | pub const Const = struct { | |
| 883 | /// Raw digits. These are: | |
| 884 | /// | |
| 885 | /// * Little-endian ordered | |
| 886 | /// * limbs.len >= 1 | |
| 887 | /// * Zero is represented as limbs.len == 1 with limbs[0] == 0. | |
| 888 | /// | |
| 889 | /// Accessing limbs directly should be avoided. | |
| 890 | limbs: []const Limb, | |
| 891 | positive: bool, | |
| 892 | ||
| 893 | /// The result is an independent resource which is managed by the caller. | |
| 894 | pub fn toManaged(self: Const, allocator: *Allocator) Allocator.Error!Managed { | |
| 895 | const limbs = try allocator.alloc(Limb, math.max(Managed.default_capacity, self.limbs.len)); | |
| 896 | mem.copy(Limb, limbs, self.limbs); | |
| 897 | return Managed{ | |
| 898 | .allocator = allocator, | |
| 899 | .limbs = limbs, | |
| 900 | .metadata = if (self.positive) | |
| 901 | self.limbs.len & ~Managed.sign_bit | |
| 902 | else | |
| 903 | self.limbs.len | Managed.sign_bit, | |
| 904 | }; | |
| 1258 | 905 | } |
| 1259 | 906 | |
| 1260 | /// r = a >> shift | |
| 1261 | pub fn shiftRight(r: *Int, a: Int, shift: usize) !void { | |
| 1262 | r.assertWritable(); | |
| 907 | /// Asserts `limbs` is big enough to store the value. | |
| 908 | pub fn toMutable(self: Const, limbs: []Limb) Mutable { | |
| 909 | mem.copy(Limb, limbs, self.limbs[0..self.limbs.len]); | |
| 910 | return .{ | |
| 911 | .limbs = limbs, | |
| 912 | .positive = self.positive, | |
| 913 | .len = self.limbs.len, | |
| 914 | }; | |
| 915 | } | |
| 1263 | 916 | |
| 1264 | if (a.len() <= shift / Limb.bit_count) { | |
| 1265 | r.metadata = 1; | |
| 1266 | r.limbs[0] = 0; | |
| 1267 | return; | |
| 917 | pub fn dump(self: Const) void { | |
| 918 | for (self.limbs[0..self.limbs.len]) |limb| { | |
| 919 | std.debug.warn("{x} ", .{limb}); | |
| 1268 | 920 | } |
| 921 | std.debug.warn("positive={}\n", .{self.positive}); | |
| 922 | } | |
| 1269 | 923 | |
| 1270 | try r.ensureCapacity(a.len() - (shift / Limb.bit_count)); | |
| 1271 | const r_len = llshr(r.limbs[0..], a.limbs[0..a.len()], shift); | |
| 1272 | r.metadata = a.len() - (shift / Limb.bit_count); | |
| 1273 | r.setSign(a.isPositive()); | |
| 924 | pub fn abs(self: Const) Const { | |
| 925 | return .{ | |
| 926 | .limbs = self.limbs, | |
| 927 | .positive = true, | |
| 928 | }; | |
| 1274 | 929 | } |
| 1275 | 930 | |
| 1276 | fn llshr(r: []Limb, a: []const Limb, shift: usize) void { | |
| 1277 | @setRuntimeSafety(false); | |
| 1278 | debug.assert(a.len >= 1); | |
| 1279 | debug.assert(r.len >= a.len - (shift / Limb.bit_count)); | |
| 931 | pub fn isOdd(self: Const) bool { | |
| 932 | return self.limbs[0] & 1 != 0; | |
| 933 | } | |
| 1280 | 934 | |
| 1281 | const limb_shift = shift / Limb.bit_count; | |
| 1282 | const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count); | |
| 935 | pub fn isEven(self: Const) bool { | |
| 936 | return !self.isOdd(); | |
| 937 | } | |
| 1283 | 938 | |
| 1284 | var carry: Limb = 0; | |
| 1285 | var i: usize = 0; | |
| 1286 | while (i < a.len - limb_shift) : (i += 1) { | |
| 1287 | const src_i = a.len - i - 1; | |
| 1288 | const dst_i = src_i - limb_shift; | |
| 1289 | ||
| 1290 | const src_digit = a[src_i]; | |
| 1291 | r[dst_i] = carry | (src_digit >> interior_limb_shift); | |
| 1292 | carry = @call(.{ .modifier = .always_inline }, math.shl, .{ | |
| 1293 | Limb, | |
| 1294 | src_digit, | |
| 1295 | Limb.bit_count - @intCast(Limb, interior_limb_shift), | |
| 1296 | }); | |
| 1297 | } | |
| 939 | /// Returns the number of bits required to represent the absolute value of an integer. | |
| 940 | pub fn bitCountAbs(self: Const) usize { | |
| 941 | return (self.limbs.len - 1) * Limb.bit_count + (Limb.bit_count - @clz(Limb, self.limbs[self.limbs.len - 1])); | |
| 1298 | 942 | } |
| 1299 | 943 | |
| 1300 | /// r = a | b | |
| 944 | /// Returns the number of bits required to represent the integer in twos-complement form. | |
| 1301 | 945 | /// |
| 1302 | /// a and b are zero-extended to the longer of a or b. | |
| 1303 | pub fn bitOr(r: *Int, a: Int, b: Int) !void { | |
| 1304 | r.assertWritable(); | |
| 946 | /// If the integer is negative the value returned is the number of bits needed by a signed | |
| 947 | /// integer to represent the value. If positive the value is the number of bits for an | |
| 948 | /// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount | |
| 949 | /// one greater than the returned value. | |
| 950 | /// | |
| 951 | /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7. | |
| 952 | pub fn bitCountTwosComp(self: Const) usize { | |
| 953 | var bits = self.bitCountAbs(); | |
| 1305 | 954 | |
| 1306 | if (a.len() > b.len()) { | |
| 1307 | try r.ensureCapacity(a.len()); | |
| 1308 | llor(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]); | |
| 1309 | r.setLen(a.len()); | |
| 1310 | } else { | |
| 1311 | try r.ensureCapacity(b.len()); | |
| 1312 | llor(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]); | |
| 1313 | r.setLen(b.len()); | |
| 955 | // If the entire value has only one bit set (e.g. 0b100000000) then the negation in twos | |
| 956 | // complement requires one less bit. | |
| 957 | if (!self.positive) block: { | |
| 958 | bits += 1; | |
| 959 | ||
| 960 | if (@popCount(Limb, self.limbs[self.limbs.len - 1]) == 1) { | |
| 961 | for (self.limbs[0 .. self.limbs.len - 1]) |limb| { | |
| 962 | if (@popCount(Limb, limb) != 0) { | |
| 963 | break :block; | |
| 964 | } | |
| 965 | } | |
| 966 | ||
| 967 | bits -= 1; | |
| 968 | } | |
| 1314 | 969 | } |
| 1315 | } | |
| 1316 | 970 | |
| 1317 | fn llor(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 1318 | @setRuntimeSafety(false); | |
| 1319 | debug.assert(r.len >= a.len); | |
| 1320 | debug.assert(a.len >= b.len); | |
| 971 | return bits; | |
| 972 | } | |
| 1321 | 973 | |
| 1322 | var i: usize = 0; | |
| 1323 | while (i < b.len) : (i += 1) { | |
| 1324 | r[i] = a[i] | b[i]; | |
| 974 | pub fn fitsInTwosComp(self: Const, is_signed: bool, bit_count: usize) bool { | |
| 975 | if (self.eqZero()) { | |
| 976 | return true; | |
| 1325 | 977 | } |
| 1326 | while (i < a.len) : (i += 1) { | |
| 1327 | r[i] = a[i]; | |
| 978 | if (!is_signed and !self.positive) { | |
| 979 | return false; | |
| 1328 | 980 | } |
| 981 | ||
| 982 | const req_bits = self.bitCountTwosComp() + @boolToInt(self.positive and is_signed); | |
| 983 | return bit_count >= req_bits; | |
| 1329 | 984 | } |
| 1330 | 985 | |
| 1331 | /// r = a & b | |
| 1332 | pub fn bitAnd(r: *Int, a: Int, b: Int) !void { | |
| 1333 | r.assertWritable(); | |
| 986 | /// Returns whether self can fit into an integer of the requested type. | |
| 987 | pub fn fits(self: Const, comptime T: type) bool { | |
| 988 | const info = @typeInfo(T).Int; | |
| 989 | return self.fitsInTwosComp(info.is_signed, info.bits); | |
| 990 | } | |
| 1334 | 991 | |
| 1335 | if (a.len() > b.len()) { | |
| 1336 | try r.ensureCapacity(b.len()); | |
| 1337 | lland(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]); | |
| 1338 | r.normalize(b.len()); | |
| 1339 | } else { | |
| 1340 | try r.ensureCapacity(a.len()); | |
| 1341 | lland(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]); | |
| 1342 | r.normalize(a.len()); | |
| 1343 | } | |
| 992 | /// Returns the approximate size of the integer in the given base. Negative values accommodate for | |
| 993 | /// the minus sign. This is used for determining the number of characters needed to print the | |
| 994 | /// value. It is inexact and may exceed the given value by ~1-2 bytes. | |
| 995 | /// TODO See if we can make this exact. | |
| 996 | pub fn sizeInBaseUpperBound(self: Const, base: usize) usize { | |
| 997 | const bit_count = @as(usize, @boolToInt(!self.positive)) + self.bitCountAbs(); | |
| 998 | return (bit_count / math.log2(base)) + 2; | |
| 1344 | 999 | } |
| 1345 | 1000 | |
| 1346 | fn lland(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 1347 | @setRuntimeSafety(false); | |
| 1348 | debug.assert(r.len >= b.len); | |
| 1349 | debug.assert(a.len >= b.len); | |
| 1001 | pub const ConvertError = error{ | |
| 1002 | NegativeIntoUnsigned, | |
| 1003 | TargetTooSmall, | |
| 1004 | }; | |
| 1005 | ||
| 1006 | /// Convert self to type T. | |
| 1007 | /// | |
| 1008 | /// Returns an error if self cannot be narrowed into the requested type without truncation. | |
| 1009 | pub fn to(self: Const, comptime T: type) ConvertError!T { | |
| 1010 | switch (@typeInfo(T)) { | |
| 1011 | .Int => { | |
| 1012 | const UT = std.meta.Int(false, T.bit_count); | |
| 1350 | 1013 | |
| 1351 | var i: usize = 0; | |
| 1352 | while (i < b.len) : (i += 1) { | |
| 1353 | r[i] = a[i] & b[i]; | |
| 1014 | if (self.bitCountTwosComp() > T.bit_count) { | |
| 1015 | return error.TargetTooSmall; | |
| 1016 | } | |
| 1017 | ||
| 1018 | var r: UT = 0; | |
| 1019 | ||
| 1020 | if (@sizeOf(UT) <= @sizeOf(Limb)) { | |
| 1021 | r = @intCast(UT, self.limbs[0]); | |
| 1022 | } else { | |
| 1023 | for (self.limbs[0..self.limbs.len]) |_, ri| { | |
| 1024 | const limb = self.limbs[self.limbs.len - ri - 1]; | |
| 1025 | r <<= Limb.bit_count; | |
| 1026 | r |= limb; | |
| 1027 | } | |
| 1028 | } | |
| 1029 | ||
| 1030 | if (!T.is_signed) { | |
| 1031 | return if (self.positive) @intCast(T, r) else error.NegativeIntoUnsigned; | |
| 1032 | } else { | |
| 1033 | if (self.positive) { | |
| 1034 | return @intCast(T, r); | |
| 1035 | } else { | |
| 1036 | if (math.cast(T, r)) |ok| { | |
| 1037 | return -ok; | |
| 1038 | } else |_| { | |
| 1039 | return minInt(T); | |
| 1040 | } | |
| 1041 | } | |
| 1042 | } | |
| 1043 | }, | |
| 1044 | else => @compileError("cannot convert Const to type " ++ @typeName(T)), | |
| 1354 | 1045 | } |
| 1355 | 1046 | } |
| 1356 | 1047 | |
| 1357 | /// r = a ^ b | |
| 1358 | pub fn bitXor(r: *Int, a: Int, b: Int) !void { | |
| 1359 | r.assertWritable(); | |
| 1048 | /// To allow `std.fmt.format` to work with this type. | |
| 1049 | /// If the integer is larger than `pow(2, 64 * @sizeOf(usize) * 8), this function will fail | |
| 1050 | /// to print the string, printing "(BigInt)" instead of a number. | |
| 1051 | /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. | |
| 1052 | /// See `toString` and `toStringAlloc` for a way to print big integers without failure. | |
| 1053 | pub fn format( | |
| 1054 | self: Const, | |
| 1055 | comptime fmt: []const u8, | |
| 1056 | options: std.fmt.FormatOptions, | |
| 1057 | out_stream: var, | |
| 1058 | ) !void { | |
| 1059 | comptime var radix = 10; | |
| 1060 | comptime var uppercase = false; | |
| 1360 | 1061 | |
| 1361 | if (a.len() > b.len()) { | |
| 1362 | try r.ensureCapacity(a.len()); | |
| 1363 | llxor(r.limbs[0..], a.limbs[0..a.len()], b.limbs[0..b.len()]); | |
| 1364 | r.normalize(a.len()); | |
| 1062 | if (fmt.len == 0 or comptime mem.eql(u8, fmt, "d")) { | |
| 1063 | radix = 10; | |
| 1064 | uppercase = false; | |
| 1065 | } else if (comptime mem.eql(u8, fmt, "b")) { | |
| 1066 | radix = 2; | |
| 1067 | uppercase = false; | |
| 1068 | } else if (comptime mem.eql(u8, fmt, "x")) { | |
| 1069 | radix = 16; | |
| 1070 | uppercase = false; | |
| 1071 | } else if (comptime mem.eql(u8, fmt, "X")) { | |
| 1072 | radix = 16; | |
| 1073 | uppercase = true; | |
| 1365 | 1074 | } else { |
| 1366 | try r.ensureCapacity(b.len()); | |
| 1367 | llxor(r.limbs[0..], b.limbs[0..b.len()], a.limbs[0..a.len()]); | |
| 1368 | r.normalize(b.len()); | |
| 1075 | @compileError("Unknown format string: '" ++ fmt ++ "'"); | |
| 1369 | 1076 | } |
| 1370 | } | |
| 1371 | 1077 | |
| 1372 | fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 1373 | @setRuntimeSafety(false); | |
| 1374 | debug.assert(r.len >= a.len); | |
| 1375 | debug.assert(a.len >= b.len); | |
| 1078 | var limbs: [128]Limb = undefined; | |
| 1079 | const needed_limbs = calcDivLimbsBufferLen(self.limbs.len, 1); | |
| 1080 | if (needed_limbs > limbs.len) | |
| 1081 | return out_stream.writeAll("(BigInt)"); | |
| 1376 | 1082 | |
| 1377 | var i: usize = 0; | |
| 1378 | while (i < b.len) : (i += 1) { | |
| 1379 | r[i] = a[i] ^ b[i]; | |
| 1380 | } | |
| 1381 | while (i < a.len) : (i += 1) { | |
| 1382 | r[i] = a[i]; | |
| 1383 | } | |
| 1083 | // This is the inverse of calcDivLimbsBufferLen | |
| 1084 | const available_len = (limbs.len / 3) - 2; | |
| 1085 | ||
| 1086 | const biggest: Const = .{ | |
| 1087 | .limbs = &([1]Limb{math.maxInt(Limb)} ** available_len), | |
| 1088 | .positive = false, | |
| 1089 | }; | |
| 1090 | var buf: [biggest.sizeInBaseUpperBound(radix)]u8 = undefined; | |
| 1091 | const len = self.toString(&buf, radix, uppercase, &limbs); | |
| 1092 | return out_stream.writeAll(buf[0..len]); | |
| 1384 | 1093 | } |
| 1385 | 1094 | |
| 1386 | pub fn gcd(rma: *Int, x: Int, y: Int) !void { | |
| 1387 | rma.assertWritable(); | |
| 1388 | var r = rma; | |
| 1389 | var aliased = rma.limbs.ptr == x.limbs.ptr or rma.limbs.ptr == y.limbs.ptr; | |
| 1095 | /// Converts self to a string in the requested base. | |
| 1096 | /// Caller owns returned memory. | |
| 1097 | /// Asserts that `base` is in the range [2, 16]. | |
| 1098 | /// See also `toString`, a lower level function than this. | |
| 1099 | pub fn toStringAlloc(self: Const, allocator: *Allocator, base: u8, uppercase: bool) Allocator.Error![]u8 { | |
| 1100 | assert(base >= 2); | |
| 1101 | assert(base <= 16); | |
| 1390 | 1102 | |
| 1391 | var sr: Int = undefined; | |
| 1392 | if (aliased) { | |
| 1393 | sr = try Int.initCapacity(rma.allocator.?, math.max(x.len(), y.len())); | |
| 1394 | r = &sr; | |
| 1395 | aliased = true; | |
| 1103 | if (self.eqZero()) { | |
| 1104 | return mem.dupe(allocator, u8, "0"); | |
| 1396 | 1105 | } |
| 1397 | defer if (aliased) { | |
| 1398 | rma.swap(r); | |
| 1399 | r.deinit(); | |
| 1400 | }; | |
| 1106 | const string = try allocator.alloc(u8, self.sizeInBaseUpperBound(base)); | |
| 1107 | errdefer allocator.free(string); | |
| 1401 | 1108 | |
| 1402 | try gcdLehmer(r, x, y); | |
| 1403 | } | |
| 1109 | const limbs = try allocator.alloc(Limb, calcToStringLimbsBufferLen(self.limbs.len, base)); | |
| 1110 | defer allocator.free(limbs); | |
| 1404 | 1111 | |
| 1405 | fn gcdLehmer(r: *Int, xa: Int, ya: Int) !void { | |
| 1406 | var x = try xa.clone(); | |
| 1407 | x.abs(); | |
| 1408 | defer x.deinit(); | |
| 1112 | return allocator.shrink(string, self.toString(string, base, uppercase, limbs)); | |
| 1113 | } | |
| 1409 | 1114 | |
| 1410 | var y = try ya.clone(); | |
| 1411 | y.abs(); | |
| 1412 | defer y.deinit(); | |
| 1115 | /// Converts self to a string in the requested base. | |
| 1116 | /// Asserts that `base` is in the range [2, 16]. | |
| 1117 | /// `string` is a caller-provided slice of at least `sizeInBaseUpperBound` bytes, | |
| 1118 | /// where the result is written to. | |
| 1119 | /// Returns the length of the string. | |
| 1120 | /// `limbs_buffer` is caller-provided memory for `toString` to use as a working area. It must have | |
| 1121 | /// length of at least `calcToStringLimbsBufferLen`. | |
| 1122 | /// In the case of power-of-two base, `limbs_buffer` is ignored. | |
| 1123 | /// See also `toStringAlloc`, a higher level function than this. | |
| 1124 | pub fn toString(self: Const, string: []u8, base: u8, uppercase: bool, limbs_buffer: []Limb) usize { | |
| 1125 | assert(base >= 2); | |
| 1126 | assert(base <= 16); | |
| 1413 | 1127 | |
| 1414 | if (x.cmp(y) == .lt) { | |
| 1415 | x.swap(&y); | |
| 1128 | if (self.eqZero()) { | |
| 1129 | string[0] = '0'; | |
| 1130 | return 1; | |
| 1416 | 1131 | } |
| 1417 | 1132 | |
| 1418 | var T = try Int.init(r.allocator.?); | |
| 1419 | defer T.deinit(); | |
| 1420 | ||
| 1421 | while (y.len() > 1) { | |
| 1422 | debug.assert(x.isPositive() and y.isPositive()); | |
| 1423 | debug.assert(x.len() >= y.len()); | |
| 1424 | ||
| 1425 | var xh: SignedDoubleLimb = x.limbs[x.len() - 1]; | |
| 1426 | var yh: SignedDoubleLimb = if (x.len() > y.len()) 0 else y.limbs[x.len() - 1]; | |
| 1133 | var digits_len: usize = 0; | |
| 1427 | 1134 | |
| 1428 | var A: SignedDoubleLimb = 1; | |
| 1429 | var B: SignedDoubleLimb = 0; | |
| 1430 | var C: SignedDoubleLimb = 0; | |
| 1431 | var D: SignedDoubleLimb = 1; | |
| 1135 | // Power of two: can do a single pass and use masks to extract digits. | |
| 1136 | if (math.isPowerOfTwo(base)) { | |
| 1137 | const base_shift = math.log2_int(Limb, base); | |
| 1432 | 1138 | |
| 1433 | while (yh + C != 0 and yh + D != 0) { | |
| 1434 | const q = @divFloor(xh + A, yh + C); | |
| 1435 | const qp = @divFloor(xh + B, yh + D); | |
| 1436 | if (q != qp) { | |
| 1437 | break; | |
| 1139 | outer: for (self.limbs[0..self.limbs.len]) |limb| { | |
| 1140 | var shift: usize = 0; | |
| 1141 | while (shift < Limb.bit_count) : (shift += base_shift) { | |
| 1142 | const r = @intCast(u8, (limb >> @intCast(Log2Limb, shift)) & @as(Limb, base - 1)); | |
| 1143 | const ch = std.fmt.digitToChar(r, uppercase); | |
| 1144 | string[digits_len] = ch; | |
| 1145 | digits_len += 1; | |
| 1146 | // If we hit the end, it must be all zeroes from here. | |
| 1147 | if (digits_len == string.len) break :outer; | |
| 1438 | 1148 | } |
| 1149 | } | |
| 1439 | 1150 | |
| 1440 | var t = A - q * C; | |
| 1441 | A = C; | |
| 1442 | C = t; | |
| 1443 | t = B - q * D; | |
| 1444 | B = D; | |
| 1445 | D = t; | |
| 1446 | ||
| 1447 | t = xh - q * yh; | |
| 1448 | xh = yh; | |
| 1449 | yh = t; | |
| 1151 | // Always will have a non-zero digit somewhere. | |
| 1152 | while (string[digits_len - 1] == '0') { | |
| 1153 | digits_len -= 1; | |
| 1154 | } | |
| 1155 | } else { | |
| 1156 | // Non power-of-two: batch divisions per word size. | |
| 1157 | const digits_per_limb = math.log(Limb, base, maxInt(Limb)); | |
| 1158 | var limb_base: Limb = 1; | |
| 1159 | var j: usize = 0; | |
| 1160 | while (j < digits_per_limb) : (j += 1) { | |
| 1161 | limb_base *= base; | |
| 1450 | 1162 | } |
| 1163 | const b: Const = .{ .limbs = &[_]Limb{limb_base}, .positive = true }; | |
| 1451 | 1164 | |
| 1452 | if (B == 0) { | |
| 1453 | // T = x % y, r is unused | |
| 1454 | try Int.divTrunc(r, &T, x, y); | |
| 1455 | debug.assert(T.isPositive()); | |
| 1165 | var q: Mutable = .{ | |
| 1166 | .limbs = limbs_buffer[0 .. self.limbs.len + 2], | |
| 1167 | .positive = true, // Make absolute by ignoring self.positive. | |
| 1168 | .len = self.limbs.len, | |
| 1169 | }; | |
| 1170 | mem.copy(Limb, q.limbs, self.limbs); | |
| 1456 | 1171 | |
| 1457 | x.swap(&y); | |
| 1458 | y.swap(&T); | |
| 1459 | } else { | |
| 1460 | var storage: [8]Limb = undefined; | |
| 1461 | const Ap = FixedIntFromSignedDoubleLimb(A, storage[0..2]); | |
| 1462 | const Bp = FixedIntFromSignedDoubleLimb(B, storage[2..4]); | |
| 1463 | const Cp = FixedIntFromSignedDoubleLimb(C, storage[4..6]); | |
| 1464 | const Dp = FixedIntFromSignedDoubleLimb(D, storage[6..8]); | |
| 1172 | var r: Mutable = .{ | |
| 1173 | .limbs = limbs_buffer[q.limbs.len..][0..self.limbs.len], | |
| 1174 | .positive = true, | |
| 1175 | .len = 1, | |
| 1176 | }; | |
| 1177 | r.limbs[0] = 0; | |
| 1465 | 1178 | |
| 1466 | // T = Ax + By | |
| 1467 | try r.mul(x, Ap); | |
| 1468 | try T.mul(y, Bp); | |
| 1469 | try T.add(r.*, T); | |
| 1179 | const rest_of_the_limbs_buf = limbs_buffer[q.limbs.len + r.limbs.len ..]; | |
| 1470 | 1180 | |
| 1471 | // u = Cx + Dy, r as u | |
| 1472 | try x.mul(x, Cp); | |
| 1473 | try r.mul(y, Dp); | |
| 1474 | try r.add(x, r.*); | |
| 1181 | while (q.len >= 2) { | |
| 1182 | // Passing an allocator here would not be helpful since this division is destroying | |
| 1183 | // information, not creating it. [TODO citation needed] | |
| 1184 | q.divTrunc(&r, q.toConst(), b, rest_of_the_limbs_buf, null); | |
| 1475 | 1185 | |
| 1476 | x.swap(&T); | |
| 1477 | y.swap(r); | |
| 1186 | var r_word = r.limbs[0]; | |
| 1187 | var i: usize = 0; | |
| 1188 | while (i < digits_per_limb) : (i += 1) { | |
| 1189 | const ch = std.fmt.digitToChar(@intCast(u8, r_word % base), uppercase); | |
| 1190 | r_word /= base; | |
| 1191 | string[digits_len] = ch; | |
| 1192 | digits_len += 1; | |
| 1193 | } | |
| 1478 | 1194 | } |
| 1479 | } | |
| 1480 | 1195 | |
| 1481 | // euclidean algorithm | |
| 1482 | debug.assert(x.cmp(y) != .lt); | |
| 1196 | { | |
| 1197 | assert(q.len == 1); | |
| 1483 | 1198 | |
| 1484 | while (!y.eqZero()) { | |
| 1485 | try Int.divTrunc(&T, r, x, y); | |
| 1486 | x.swap(&y); | |
| 1487 | y.swap(r); | |
| 1199 | var r_word = q.limbs[0]; | |
| 1200 | while (r_word != 0) { | |
| 1201 | const ch = std.fmt.digitToChar(@intCast(u8, r_word % base), uppercase); | |
| 1202 | r_word /= base; | |
| 1203 | string[digits_len] = ch; | |
| 1204 | digits_len += 1; | |
| 1205 | } | |
| 1206 | } | |
| 1488 | 1207 | } |
| 1489 | 1208 | |
| 1490 | r.swap(&x); | |
| 1491 | } | |
| 1492 | }; | |
| 1493 | ||
| 1494 | // Storage must live for the lifetime of the returned value | |
| 1495 | fn FixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Int { | |
| 1496 | std.debug.assert(storage.len >= 2); | |
| 1497 | ||
| 1498 | var A_is_positive = A >= 0; | |
| 1499 | const Au = @intCast(DoubleLimb, if (A < 0) -A else A); | |
| 1500 | storage[0] = @truncate(Limb, Au); | |
| 1501 | storage[1] = @truncate(Limb, Au >> Limb.bit_count); | |
| 1502 | var Ap = Int.initFixed(storage[0..2]); | |
| 1503 | Ap.setSign(A_is_positive); | |
| 1504 | return Ap; | |
| 1505 | } | |
| 1506 | ||
| 1507 | // NOTE: All the following tests assume the max machine-word will be 64-bit. | |
| 1508 | // | |
| 1509 | // They will still run on larger than this and should pass, but the multi-limb code-paths | |
| 1510 | // may be untested in some cases. | |
| 1511 | ||
| 1512 | test "big.int comptime_int set" { | |
| 1513 | comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab; | |
| 1514 | var a = try Int.initSet(testing.allocator, s); | |
| 1515 | defer a.deinit(); | |
| 1516 | ||
| 1517 | const s_limb_count = 128 / Limb.bit_count; | |
| 1209 | if (!self.positive) { | |
| 1210 | string[digits_len] = '-'; | |
| 1211 | digits_len += 1; | |
| 1212 | } | |
| 1518 | 1213 | |
| 1519 | comptime var i: usize = 0; | |
| 1520 | inline while (i < s_limb_count) : (i += 1) { | |
| 1521 | const result = @as(Limb, s & maxInt(Limb)); | |
| 1522 | s >>= Limb.bit_count / 2; | |
| 1523 | s >>= Limb.bit_count / 2; | |
| 1524 | testing.expect(a.limbs[i] == result); | |
| 1214 | const s = string[0..digits_len]; | |
| 1215 | mem.reverse(u8, s); | |
| 1216 | return s.len; | |
| 1525 | 1217 | } |
| 1526 | } | |
| 1527 | ||
| 1528 | test "big.int comptime_int set negative" { | |
| 1529 | var a = try Int.initSet(testing.allocator, -10); | |
| 1530 | defer a.deinit(); | |
| 1531 | ||
| 1532 | testing.expect(a.limbs[0] == 10); | |
| 1533 | testing.expect(a.isPositive() == false); | |
| 1534 | } | |
| 1535 | ||
| 1536 | test "big.int int set unaligned small" { | |
| 1537 | var a = try Int.initSet(testing.allocator, @as(u7, 45)); | |
| 1538 | defer a.deinit(); | |
| 1539 | ||
| 1540 | testing.expect(a.limbs[0] == 45); | |
| 1541 | testing.expect(a.isPositive() == true); | |
| 1542 | } | |
| 1543 | ||
| 1544 | test "big.int comptime_int to" { | |
| 1545 | const a = try Int.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab); | |
| 1546 | defer a.deinit(); | |
| 1547 | ||
| 1548 | testing.expect((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab); | |
| 1549 | } | |
| 1550 | ||
| 1551 | test "big.int sub-limb to" { | |
| 1552 | const a = try Int.initSet(testing.allocator, 10); | |
| 1553 | defer a.deinit(); | |
| 1554 | ||
| 1555 | testing.expect((try a.to(u8)) == 10); | |
| 1556 | } | |
| 1557 | ||
| 1558 | test "big.int to target too small error" { | |
| 1559 | const a = try Int.initSet(testing.allocator, 0xffffffff); | |
| 1560 | defer a.deinit(); | |
| 1561 | ||
| 1562 | testing.expectError(error.TargetTooSmall, a.to(u8)); | |
| 1563 | } | |
| 1564 | ||
| 1565 | test "big.int normalize" { | |
| 1566 | var a = try Int.init(testing.allocator); | |
| 1567 | defer a.deinit(); | |
| 1568 | try a.ensureCapacity(8); | |
| 1569 | ||
| 1570 | a.limbs[0] = 1; | |
| 1571 | a.limbs[1] = 2; | |
| 1572 | a.limbs[2] = 3; | |
| 1573 | a.limbs[3] = 0; | |
| 1574 | a.normalize(4); | |
| 1575 | testing.expect(a.len() == 3); | |
| 1576 | ||
| 1577 | a.limbs[0] = 1; | |
| 1578 | a.limbs[1] = 2; | |
| 1579 | a.limbs[2] = 3; | |
| 1580 | a.normalize(3); | |
| 1581 | testing.expect(a.len() == 3); | |
| 1582 | ||
| 1583 | a.limbs[0] = 0; | |
| 1584 | a.limbs[1] = 0; | |
| 1585 | a.normalize(2); | |
| 1586 | testing.expect(a.len() == 1); | |
| 1587 | ||
| 1588 | a.limbs[0] = 0; | |
| 1589 | a.normalize(1); | |
| 1590 | testing.expect(a.len() == 1); | |
| 1591 | } | |
| 1592 | ||
| 1593 | test "big.int normalize multi" { | |
| 1594 | var a = try Int.init(testing.allocator); | |
| 1595 | defer a.deinit(); | |
| 1596 | try a.ensureCapacity(8); | |
| 1597 | ||
| 1598 | a.limbs[0] = 1; | |
| 1599 | a.limbs[1] = 2; | |
| 1600 | a.limbs[2] = 0; | |
| 1601 | a.limbs[3] = 0; | |
| 1602 | a.normalize(4); | |
| 1603 | testing.expect(a.len() == 2); | |
| 1604 | ||
| 1605 | a.limbs[0] = 1; | |
| 1606 | a.limbs[1] = 2; | |
| 1607 | a.limbs[2] = 3; | |
| 1608 | a.normalize(3); | |
| 1609 | testing.expect(a.len() == 3); | |
| 1610 | ||
| 1611 | a.limbs[0] = 0; | |
| 1612 | a.limbs[1] = 0; | |
| 1613 | a.limbs[2] = 0; | |
| 1614 | a.limbs[3] = 0; | |
| 1615 | a.normalize(4); | |
| 1616 | testing.expect(a.len() == 1); | |
| 1617 | ||
| 1618 | a.limbs[0] = 0; | |
| 1619 | a.normalize(1); | |
| 1620 | testing.expect(a.len() == 1); | |
| 1621 | } | |
| 1622 | ||
| 1623 | test "big.int parity" { | |
| 1624 | var a = try Int.init(testing.allocator); | |
| 1625 | defer a.deinit(); | |
| 1626 | ||
| 1627 | try a.set(0); | |
| 1628 | testing.expect(a.isEven()); | |
| 1629 | testing.expect(!a.isOdd()); | |
| 1630 | ||
| 1631 | try a.set(7); | |
| 1632 | testing.expect(!a.isEven()); | |
| 1633 | testing.expect(a.isOdd()); | |
| 1634 | } | |
| 1635 | ||
| 1636 | test "big.int bitcount + sizeInBase" { | |
| 1637 | var a = try Int.init(testing.allocator); | |
| 1638 | defer a.deinit(); | |
| 1639 | ||
| 1640 | try a.set(0b100); | |
| 1641 | testing.expect(a.bitCountAbs() == 3); | |
| 1642 | testing.expect(a.sizeInBase(2) >= 3); | |
| 1643 | testing.expect(a.sizeInBase(10) >= 1); | |
| 1644 | ||
| 1645 | a.negate(); | |
| 1646 | testing.expect(a.bitCountAbs() == 3); | |
| 1647 | testing.expect(a.sizeInBase(2) >= 4); | |
| 1648 | testing.expect(a.sizeInBase(10) >= 2); | |
| 1649 | ||
| 1650 | try a.set(0xffffffff); | |
| 1651 | testing.expect(a.bitCountAbs() == 32); | |
| 1652 | testing.expect(a.sizeInBase(2) >= 32); | |
| 1653 | testing.expect(a.sizeInBase(10) >= 10); | |
| 1654 | ||
| 1655 | try a.shiftLeft(a, 5000); | |
| 1656 | testing.expect(a.bitCountAbs() == 5032); | |
| 1657 | testing.expect(a.sizeInBase(2) >= 5032); | |
| 1658 | a.setSign(false); | |
| 1659 | ||
| 1660 | testing.expect(a.bitCountAbs() == 5032); | |
| 1661 | testing.expect(a.sizeInBase(2) >= 5033); | |
| 1662 | } | |
| 1663 | ||
| 1664 | test "big.int bitcount/to" { | |
| 1665 | var a = try Int.init(testing.allocator); | |
| 1666 | defer a.deinit(); | |
| 1667 | 1218 | |
| 1668 | try a.set(0); | |
| 1669 | testing.expect(a.bitCountTwosComp() == 0); | |
| 1670 | ||
| 1671 | testing.expect((try a.to(u0)) == 0); | |
| 1672 | testing.expect((try a.to(i0)) == 0); | |
| 1673 | ||
| 1674 | try a.set(-1); | |
| 1675 | testing.expect(a.bitCountTwosComp() == 1); | |
| 1676 | testing.expect((try a.to(i1)) == -1); | |
| 1677 | ||
| 1678 | try a.set(-8); | |
| 1679 | testing.expect(a.bitCountTwosComp() == 4); | |
| 1680 | testing.expect((try a.to(i4)) == -8); | |
| 1681 | ||
| 1682 | try a.set(127); | |
| 1683 | testing.expect(a.bitCountTwosComp() == 7); | |
| 1684 | testing.expect((try a.to(u7)) == 127); | |
| 1685 | ||
| 1686 | try a.set(-128); | |
| 1687 | testing.expect(a.bitCountTwosComp() == 8); | |
| 1688 | testing.expect((try a.to(i8)) == -128); | |
| 1689 | ||
| 1690 | try a.set(-129); | |
| 1691 | testing.expect(a.bitCountTwosComp() == 9); | |
| 1692 | testing.expect((try a.to(i9)) == -129); | |
| 1693 | } | |
| 1694 | ||
| 1695 | test "big.int fits" { | |
| 1696 | var a = try Int.init(testing.allocator); | |
| 1697 | defer a.deinit(); | |
| 1698 | ||
| 1699 | try a.set(0); | |
| 1700 | testing.expect(a.fits(u0)); | |
| 1701 | testing.expect(a.fits(i0)); | |
| 1702 | ||
| 1703 | try a.set(255); | |
| 1704 | testing.expect(!a.fits(u0)); | |
| 1705 | testing.expect(!a.fits(u1)); | |
| 1706 | testing.expect(!a.fits(i8)); | |
| 1707 | testing.expect(a.fits(u8)); | |
| 1708 | testing.expect(a.fits(u9)); | |
| 1709 | testing.expect(a.fits(i9)); | |
| 1710 | ||
| 1711 | try a.set(-128); | |
| 1712 | testing.expect(!a.fits(i7)); | |
| 1713 | testing.expect(a.fits(i8)); | |
| 1714 | testing.expect(a.fits(i9)); | |
| 1715 | testing.expect(!a.fits(u9)); | |
| 1716 | ||
| 1717 | try a.set(0x1ffffffffeeeeeeee); | |
| 1718 | testing.expect(!a.fits(u32)); | |
| 1719 | testing.expect(!a.fits(u64)); | |
| 1720 | testing.expect(a.fits(u65)); | |
| 1721 | } | |
| 1722 | ||
| 1723 | test "big.int string set" { | |
| 1724 | var a = try Int.init(testing.allocator); | |
| 1725 | defer a.deinit(); | |
| 1726 | ||
| 1727 | try a.setString(10, "120317241209124781241290847124"); | |
| 1728 | testing.expect((try a.to(u128)) == 120317241209124781241290847124); | |
| 1729 | } | |
| 1730 | ||
| 1731 | test "big.int string negative" { | |
| 1732 | var a = try Int.init(testing.allocator); | |
| 1733 | defer a.deinit(); | |
| 1734 | ||
| 1735 | try a.setString(10, "-1023"); | |
| 1736 | testing.expect((try a.to(i32)) == -1023); | |
| 1737 | } | |
| 1738 | ||
| 1739 | test "big.int string set number with underscores" { | |
| 1740 | var a = try Int.init(testing.allocator); | |
| 1741 | defer a.deinit(); | |
| 1742 | ||
| 1743 | try a.setString(10, "__1_2_0_3_1_7_2_4_1_2_0_____9_1__2__4_7_8_1_2_4_1_2_9_0_8_4_7_1_2_4___"); | |
| 1744 | testing.expect((try a.to(u128)) == 120317241209124781241290847124); | |
| 1745 | } | |
| 1746 | ||
| 1747 | test "big.int string set case insensitive number" { | |
| 1748 | var a = try Int.init(testing.allocator); | |
| 1749 | defer a.deinit(); | |
| 1750 | ||
| 1751 | try a.setString(16, "aB_cD_eF"); | |
| 1752 | testing.expect((try a.to(u32)) == 0xabcdef); | |
| 1753 | } | |
| 1754 | ||
| 1755 | test "big.int string set bad char error" { | |
| 1756 | var a = try Int.init(testing.allocator); | |
| 1757 | defer a.deinit(); | |
| 1758 | testing.expectError(error.InvalidCharForDigit, a.setString(10, "x")); | |
| 1759 | } | |
| 1760 | ||
| 1761 | test "big.int string set bad base error" { | |
| 1762 | var a = try Int.init(testing.allocator); | |
| 1763 | defer a.deinit(); | |
| 1764 | testing.expectError(error.InvalidBase, a.setString(45, "10")); | |
| 1765 | } | |
| 1766 | ||
| 1767 | test "big.int string to" { | |
| 1768 | const a = try Int.initSet(testing.allocator, 120317241209124781241290847124); | |
| 1769 | defer a.deinit(); | |
| 1770 | ||
| 1771 | const as = try a.toString(testing.allocator, 10, false); | |
| 1772 | defer testing.allocator.free(as); | |
| 1773 | const es = "120317241209124781241290847124"; | |
| 1774 | ||
| 1775 | testing.expect(mem.eql(u8, as, es)); | |
| 1776 | } | |
| 1777 | ||
| 1778 | test "big.int string to base base error" { | |
| 1779 | const a = try Int.initSet(testing.allocator, 0xffffffff); | |
| 1780 | defer a.deinit(); | |
| 1781 | ||
| 1782 | testing.expectError(error.InvalidBase, a.toString(testing.allocator, 45, false)); | |
| 1783 | } | |
| 1784 | ||
| 1785 | test "big.int string to base 2" { | |
| 1786 | const a = try Int.initSet(testing.allocator, -0b1011); | |
| 1787 | defer a.deinit(); | |
| 1788 | ||
| 1789 | const as = try a.toString(testing.allocator, 2, false); | |
| 1790 | defer testing.allocator.free(as); | |
| 1791 | const es = "-1011"; | |
| 1792 | ||
| 1793 | testing.expect(mem.eql(u8, as, es)); | |
| 1794 | } | |
| 1795 | ||
| 1796 | test "big.int string to base 16" { | |
| 1797 | const a = try Int.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab); | |
| 1798 | defer a.deinit(); | |
| 1799 | ||
| 1800 | const as = try a.toString(testing.allocator, 16, false); | |
| 1801 | defer testing.allocator.free(as); | |
| 1802 | const es = "efffffff00000001eeeeeeefaaaaaaab"; | |
| 1803 | ||
| 1804 | testing.expect(mem.eql(u8, as, es)); | |
| 1805 | } | |
| 1806 | ||
| 1807 | test "big.int neg string to" { | |
| 1808 | const a = try Int.initSet(testing.allocator, -123907434); | |
| 1809 | defer a.deinit(); | |
| 1810 | ||
| 1811 | const as = try a.toString(testing.allocator, 10, false); | |
| 1812 | defer testing.allocator.free(as); | |
| 1813 | const es = "-123907434"; | |
| 1814 | ||
| 1815 | testing.expect(mem.eql(u8, as, es)); | |
| 1816 | } | |
| 1817 | ||
| 1818 | test "big.int zero string to" { | |
| 1819 | const a = try Int.initSet(testing.allocator, 0); | |
| 1820 | defer a.deinit(); | |
| 1821 | ||
| 1822 | const as = try a.toString(testing.allocator, 10, false); | |
| 1823 | defer testing.allocator.free(as); | |
| 1824 | const es = "0"; | |
| 1825 | ||
| 1826 | testing.expect(mem.eql(u8, as, es)); | |
| 1827 | } | |
| 1828 | ||
| 1829 | test "big.int clone" { | |
| 1830 | var a = try Int.initSet(testing.allocator, 1234); | |
| 1831 | defer a.deinit(); | |
| 1832 | const b = try a.clone(); | |
| 1833 | defer b.deinit(); | |
| 1834 | ||
| 1835 | testing.expect((try a.to(u32)) == 1234); | |
| 1836 | testing.expect((try b.to(u32)) == 1234); | |
| 1837 | ||
| 1838 | try a.set(77); | |
| 1839 | testing.expect((try a.to(u32)) == 77); | |
| 1840 | testing.expect((try b.to(u32)) == 1234); | |
| 1841 | } | |
| 1842 | ||
| 1843 | test "big.int swap" { | |
| 1844 | var a = try Int.initSet(testing.allocator, 1234); | |
| 1845 | defer a.deinit(); | |
| 1846 | var b = try Int.initSet(testing.allocator, 5678); | |
| 1847 | defer b.deinit(); | |
| 1848 | ||
| 1849 | testing.expect((try a.to(u32)) == 1234); | |
| 1850 | testing.expect((try b.to(u32)) == 5678); | |
| 1851 | ||
| 1852 | a.swap(&b); | |
| 1853 | ||
| 1854 | testing.expect((try a.to(u32)) == 5678); | |
| 1855 | testing.expect((try b.to(u32)) == 1234); | |
| 1856 | } | |
| 1857 | ||
| 1858 | test "big.int to negative" { | |
| 1859 | var a = try Int.initSet(testing.allocator, -10); | |
| 1860 | defer a.deinit(); | |
| 1861 | ||
| 1862 | testing.expect((try a.to(i32)) == -10); | |
| 1863 | } | |
| 1864 | ||
| 1865 | test "big.int compare" { | |
| 1866 | var a = try Int.initSet(testing.allocator, -11); | |
| 1867 | defer a.deinit(); | |
| 1868 | var b = try Int.initSet(testing.allocator, 10); | |
| 1869 | defer b.deinit(); | |
| 1870 | ||
| 1871 | testing.expect(a.cmpAbs(b) == .gt); | |
| 1872 | testing.expect(a.cmp(b) == .lt); | |
| 1873 | } | |
| 1874 | ||
| 1875 | test "big.int compare similar" { | |
| 1876 | var a = try Int.initSet(testing.allocator, 0xffffffffeeeeeeeeffffffffeeeeeeee); | |
| 1877 | defer a.deinit(); | |
| 1878 | var b = try Int.initSet(testing.allocator, 0xffffffffeeeeeeeeffffffffeeeeeeef); | |
| 1879 | defer b.deinit(); | |
| 1880 | ||
| 1881 | testing.expect(a.cmpAbs(b) == .lt); | |
| 1882 | testing.expect(b.cmpAbs(a) == .gt); | |
| 1883 | } | |
| 1884 | ||
| 1885 | test "big.int compare different limb size" { | |
| 1886 | var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 1887 | defer a.deinit(); | |
| 1888 | var b = try Int.initSet(testing.allocator, 1); | |
| 1889 | defer b.deinit(); | |
| 1890 | ||
| 1891 | testing.expect(a.cmpAbs(b) == .gt); | |
| 1892 | testing.expect(b.cmpAbs(a) == .lt); | |
| 1893 | } | |
| 1894 | ||
| 1895 | test "big.int compare multi-limb" { | |
| 1896 | var a = try Int.initSet(testing.allocator, -0x7777777799999999ffffeeeeffffeeeeffffeeeef); | |
| 1897 | defer a.deinit(); | |
| 1898 | var b = try Int.initSet(testing.allocator, 0x7777777799999999ffffeeeeffffeeeeffffeeeee); | |
| 1899 | defer b.deinit(); | |
| 1900 | ||
| 1901 | testing.expect(a.cmpAbs(b) == .gt); | |
| 1902 | testing.expect(a.cmp(b) == .lt); | |
| 1903 | } | |
| 1904 | ||
| 1905 | test "big.int equality" { | |
| 1906 | var a = try Int.initSet(testing.allocator, 0xffffffff1); | |
| 1907 | defer a.deinit(); | |
| 1908 | var b = try Int.initSet(testing.allocator, -0xffffffff1); | |
| 1909 | defer b.deinit(); | |
| 1910 | ||
| 1911 | testing.expect(a.eqAbs(b)); | |
| 1912 | testing.expect(!a.eq(b)); | |
| 1913 | } | |
| 1914 | ||
| 1915 | test "big.int abs" { | |
| 1916 | var a = try Int.initSet(testing.allocator, -5); | |
| 1917 | defer a.deinit(); | |
| 1918 | ||
| 1919 | a.abs(); | |
| 1920 | testing.expect((try a.to(u32)) == 5); | |
| 1921 | ||
| 1922 | a.abs(); | |
| 1923 | testing.expect((try a.to(u32)) == 5); | |
| 1924 | } | |
| 1925 | ||
| 1926 | test "big.int negate" { | |
| 1927 | var a = try Int.initSet(testing.allocator, 5); | |
| 1928 | defer a.deinit(); | |
| 1929 | ||
| 1930 | a.negate(); | |
| 1931 | testing.expect((try a.to(i32)) == -5); | |
| 1932 | ||
| 1933 | a.negate(); | |
| 1934 | testing.expect((try a.to(i32)) == 5); | |
| 1935 | } | |
| 1936 | ||
| 1937 | test "big.int add single-single" { | |
| 1938 | var a = try Int.initSet(testing.allocator, 50); | |
| 1939 | defer a.deinit(); | |
| 1940 | var b = try Int.initSet(testing.allocator, 5); | |
| 1941 | defer b.deinit(); | |
| 1942 | ||
| 1943 | var c = try Int.init(testing.allocator); | |
| 1944 | defer c.deinit(); | |
| 1945 | try c.add(a, b); | |
| 1946 | ||
| 1947 | testing.expect((try c.to(u32)) == 55); | |
| 1948 | } | |
| 1949 | ||
| 1950 | test "big.int add multi-single" { | |
| 1951 | var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 1952 | defer a.deinit(); | |
| 1953 | var b = try Int.initSet(testing.allocator, 1); | |
| 1954 | defer b.deinit(); | |
| 1955 | ||
| 1956 | var c = try Int.init(testing.allocator); | |
| 1957 | defer c.deinit(); | |
| 1958 | ||
| 1959 | try c.add(a, b); | |
| 1960 | testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2); | |
| 1961 | ||
| 1962 | try c.add(b, a); | |
| 1963 | testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2); | |
| 1964 | } | |
| 1965 | ||
| 1966 | test "big.int add multi-multi" { | |
| 1967 | const op1 = 0xefefefef7f7f7f7f; | |
| 1968 | const op2 = 0xfefefefe9f9f9f9f; | |
| 1969 | var a = try Int.initSet(testing.allocator, op1); | |
| 1970 | defer a.deinit(); | |
| 1971 | var b = try Int.initSet(testing.allocator, op2); | |
| 1972 | defer b.deinit(); | |
| 1973 | ||
| 1974 | var c = try Int.init(testing.allocator); | |
| 1975 | defer c.deinit(); | |
| 1976 | try c.add(a, b); | |
| 1977 | ||
| 1978 | testing.expect((try c.to(u128)) == op1 + op2); | |
| 1979 | } | |
| 1980 | ||
| 1981 | test "big.int add zero-zero" { | |
| 1982 | var a = try Int.initSet(testing.allocator, 0); | |
| 1983 | defer a.deinit(); | |
| 1984 | var b = try Int.initSet(testing.allocator, 0); | |
| 1985 | defer b.deinit(); | |
| 1986 | ||
| 1987 | var c = try Int.init(testing.allocator); | |
| 1988 | defer c.deinit(); | |
| 1989 | try c.add(a, b); | |
| 1990 | ||
| 1991 | testing.expect((try c.to(u32)) == 0); | |
| 1992 | } | |
| 1993 | ||
| 1994 | test "big.int add alias multi-limb nonzero-zero" { | |
| 1995 | const op1 = 0xffffffff777777771; | |
| 1996 | var a = try Int.initSet(testing.allocator, op1); | |
| 1997 | defer a.deinit(); | |
| 1998 | var b = try Int.initSet(testing.allocator, 0); | |
| 1999 | defer b.deinit(); | |
| 2000 | ||
| 2001 | try a.add(a, b); | |
| 2002 | ||
| 2003 | testing.expect((try a.to(u128)) == op1); | |
| 2004 | } | |
| 2005 | ||
| 2006 | test "big.int add sign" { | |
| 2007 | var a = try Int.init(testing.allocator); | |
| 2008 | defer a.deinit(); | |
| 2009 | ||
| 2010 | const one = try Int.initSet(testing.allocator, 1); | |
| 2011 | defer one.deinit(); | |
| 2012 | const two = try Int.initSet(testing.allocator, 2); | |
| 2013 | defer two.deinit(); | |
| 2014 | const neg_one = try Int.initSet(testing.allocator, -1); | |
| 2015 | defer neg_one.deinit(); | |
| 2016 | const neg_two = try Int.initSet(testing.allocator, -2); | |
| 2017 | defer neg_two.deinit(); | |
| 2018 | ||
| 2019 | try a.add(one, two); | |
| 2020 | testing.expect((try a.to(i32)) == 3); | |
| 2021 | ||
| 2022 | try a.add(neg_one, two); | |
| 2023 | testing.expect((try a.to(i32)) == 1); | |
| 2024 | ||
| 2025 | try a.add(one, neg_two); | |
| 2026 | testing.expect((try a.to(i32)) == -1); | |
| 2027 | ||
| 2028 | try a.add(neg_one, neg_two); | |
| 2029 | testing.expect((try a.to(i32)) == -3); | |
| 2030 | } | |
| 2031 | ||
| 2032 | test "big.int sub single-single" { | |
| 2033 | var a = try Int.initSet(testing.allocator, 50); | |
| 2034 | defer a.deinit(); | |
| 2035 | var b = try Int.initSet(testing.allocator, 5); | |
| 2036 | defer b.deinit(); | |
| 2037 | ||
| 2038 | var c = try Int.init(testing.allocator); | |
| 2039 | defer c.deinit(); | |
| 2040 | try c.sub(a, b); | |
| 2041 | ||
| 2042 | testing.expect((try c.to(u32)) == 45); | |
| 2043 | } | |
| 2044 | ||
| 2045 | test "big.int sub multi-single" { | |
| 2046 | var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 2047 | defer a.deinit(); | |
| 2048 | var b = try Int.initSet(testing.allocator, 1); | |
| 2049 | defer b.deinit(); | |
| 2050 | ||
| 2051 | var c = try Int.init(testing.allocator); | |
| 2052 | defer c.deinit(); | |
| 2053 | try c.sub(a, b); | |
| 2054 | ||
| 2055 | testing.expect((try c.to(Limb)) == maxInt(Limb)); | |
| 2056 | } | |
| 2057 | ||
| 2058 | test "big.int sub multi-multi" { | |
| 2059 | const op1 = 0xefefefefefefefefefefefef; | |
| 2060 | const op2 = 0xabababababababababababab; | |
| 2061 | ||
| 2062 | var a = try Int.initSet(testing.allocator, op1); | |
| 2063 | defer a.deinit(); | |
| 2064 | var b = try Int.initSet(testing.allocator, op2); | |
| 2065 | defer b.deinit(); | |
| 2066 | ||
| 2067 | var c = try Int.init(testing.allocator); | |
| 2068 | defer c.deinit(); | |
| 2069 | try c.sub(a, b); | |
| 2070 | ||
| 2071 | testing.expect((try c.to(u128)) == op1 - op2); | |
| 2072 | } | |
| 2073 | ||
| 2074 | test "big.int sub equal" { | |
| 2075 | var a = try Int.initSet(testing.allocator, 0x11efefefefefefefefefefefef); | |
| 2076 | defer a.deinit(); | |
| 2077 | var b = try Int.initSet(testing.allocator, 0x11efefefefefefefefefefefef); | |
| 2078 | defer b.deinit(); | |
| 2079 | ||
| 2080 | var c = try Int.init(testing.allocator); | |
| 2081 | defer c.deinit(); | |
| 2082 | try c.sub(a, b); | |
| 2083 | ||
| 2084 | testing.expect((try c.to(u32)) == 0); | |
| 2085 | } | |
| 2086 | ||
| 2087 | test "big.int sub sign" { | |
| 2088 | var a = try Int.init(testing.allocator); | |
| 2089 | defer a.deinit(); | |
| 2090 | ||
| 2091 | const one = try Int.initSet(testing.allocator, 1); | |
| 2092 | defer one.deinit(); | |
| 2093 | const two = try Int.initSet(testing.allocator, 2); | |
| 2094 | defer two.deinit(); | |
| 2095 | const neg_one = try Int.initSet(testing.allocator, -1); | |
| 2096 | defer neg_one.deinit(); | |
| 2097 | const neg_two = try Int.initSet(testing.allocator, -2); | |
| 2098 | defer neg_two.deinit(); | |
| 2099 | ||
| 2100 | try a.sub(one, two); | |
| 2101 | testing.expect((try a.to(i32)) == -1); | |
| 2102 | ||
| 2103 | try a.sub(neg_one, two); | |
| 2104 | testing.expect((try a.to(i32)) == -3); | |
| 2105 | ||
| 2106 | try a.sub(one, neg_two); | |
| 2107 | testing.expect((try a.to(i32)) == 3); | |
| 2108 | ||
| 2109 | try a.sub(neg_one, neg_two); | |
| 2110 | testing.expect((try a.to(i32)) == 1); | |
| 2111 | ||
| 2112 | try a.sub(neg_two, neg_one); | |
| 2113 | testing.expect((try a.to(i32)) == -1); | |
| 2114 | } | |
| 2115 | ||
| 2116 | test "big.int mul single-single" { | |
| 2117 | var a = try Int.initSet(testing.allocator, 50); | |
| 2118 | defer a.deinit(); | |
| 2119 | var b = try Int.initSet(testing.allocator, 5); | |
| 2120 | defer b.deinit(); | |
| 2121 | ||
| 2122 | var c = try Int.init(testing.allocator); | |
| 2123 | defer c.deinit(); | |
| 2124 | try c.mul(a, b); | |
| 2125 | ||
| 2126 | testing.expect((try c.to(u64)) == 250); | |
| 2127 | } | |
| 2128 | ||
| 2129 | test "big.int mul multi-single" { | |
| 2130 | var a = try Int.initSet(testing.allocator, maxInt(Limb)); | |
| 2131 | defer a.deinit(); | |
| 2132 | var b = try Int.initSet(testing.allocator, 2); | |
| 2133 | defer b.deinit(); | |
| 2134 | ||
| 2135 | var c = try Int.init(testing.allocator); | |
| 2136 | defer c.deinit(); | |
| 2137 | try c.mul(a, b); | |
| 2138 | ||
| 2139 | testing.expect((try c.to(DoubleLimb)) == 2 * maxInt(Limb)); | |
| 2140 | } | |
| 2141 | ||
| 2142 | test "big.int mul multi-multi" { | |
| 2143 | const op1 = 0x998888efefefefefefefef; | |
| 2144 | const op2 = 0x333000abababababababab; | |
| 2145 | var a = try Int.initSet(testing.allocator, op1); | |
| 2146 | defer a.deinit(); | |
| 2147 | var b = try Int.initSet(testing.allocator, op2); | |
| 2148 | defer b.deinit(); | |
| 2149 | ||
| 2150 | var c = try Int.init(testing.allocator); | |
| 2151 | defer c.deinit(); | |
| 2152 | try c.mul(a, b); | |
| 2153 | ||
| 2154 | testing.expect((try c.to(u256)) == op1 * op2); | |
| 2155 | } | |
| 2156 | ||
| 2157 | test "big.int mul alias r with a" { | |
| 2158 | var a = try Int.initSet(testing.allocator, maxInt(Limb)); | |
| 2159 | defer a.deinit(); | |
| 2160 | var b = try Int.initSet(testing.allocator, 2); | |
| 2161 | defer b.deinit(); | |
| 2162 | ||
| 2163 | try a.mul(a, b); | |
| 2164 | ||
| 2165 | testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb)); | |
| 2166 | } | |
| 2167 | ||
| 2168 | test "big.int mul alias r with b" { | |
| 2169 | var a = try Int.initSet(testing.allocator, maxInt(Limb)); | |
| 2170 | defer a.deinit(); | |
| 2171 | var b = try Int.initSet(testing.allocator, 2); | |
| 2172 | defer b.deinit(); | |
| 2173 | ||
| 2174 | try a.mul(b, a); | |
| 2175 | ||
| 2176 | testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb)); | |
| 2177 | } | |
| 2178 | ||
| 2179 | test "big.int mul alias r with a and b" { | |
| 2180 | var a = try Int.initSet(testing.allocator, maxInt(Limb)); | |
| 2181 | defer a.deinit(); | |
| 2182 | ||
| 2183 | try a.mul(a, a); | |
| 2184 | ||
| 2185 | testing.expect((try a.to(DoubleLimb)) == maxInt(Limb) * maxInt(Limb)); | |
| 2186 | } | |
| 2187 | ||
| 2188 | test "big.int mul a*0" { | |
| 2189 | var a = try Int.initSet(testing.allocator, 0xefefefefefefefef); | |
| 2190 | defer a.deinit(); | |
| 2191 | var b = try Int.initSet(testing.allocator, 0); | |
| 2192 | defer b.deinit(); | |
| 2193 | ||
| 2194 | var c = try Int.init(testing.allocator); | |
| 2195 | defer c.deinit(); | |
| 2196 | try c.mul(a, b); | |
| 2197 | ||
| 2198 | testing.expect((try c.to(u32)) == 0); | |
| 2199 | } | |
| 2200 | ||
| 2201 | test "big.int mul 0*0" { | |
| 2202 | var a = try Int.initSet(testing.allocator, 0); | |
| 2203 | defer a.deinit(); | |
| 2204 | var b = try Int.initSet(testing.allocator, 0); | |
| 2205 | defer b.deinit(); | |
| 2206 | ||
| 2207 | var c = try Int.init(testing.allocator); | |
| 2208 | defer c.deinit(); | |
| 2209 | try c.mul(a, b); | |
| 2210 | ||
| 2211 | testing.expect((try c.to(u32)) == 0); | |
| 2212 | } | |
| 2213 | ||
| 2214 | test "big.int div single-single no rem" { | |
| 2215 | var a = try Int.initSet(testing.allocator, 50); | |
| 2216 | defer a.deinit(); | |
| 2217 | var b = try Int.initSet(testing.allocator, 5); | |
| 2218 | defer b.deinit(); | |
| 2219 | ||
| 2220 | var q = try Int.init(testing.allocator); | |
| 2221 | defer q.deinit(); | |
| 2222 | var r = try Int.init(testing.allocator); | |
| 2223 | defer r.deinit(); | |
| 2224 | try Int.divTrunc(&q, &r, a, b); | |
| 2225 | ||
| 2226 | testing.expect((try q.to(u32)) == 10); | |
| 2227 | testing.expect((try r.to(u32)) == 0); | |
| 2228 | } | |
| 2229 | ||
| 2230 | test "big.int div single-single with rem" { | |
| 2231 | var a = try Int.initSet(testing.allocator, 49); | |
| 2232 | defer a.deinit(); | |
| 2233 | var b = try Int.initSet(testing.allocator, 5); | |
| 2234 | defer b.deinit(); | |
| 2235 | ||
| 2236 | var q = try Int.init(testing.allocator); | |
| 2237 | defer q.deinit(); | |
| 2238 | var r = try Int.init(testing.allocator); | |
| 2239 | defer r.deinit(); | |
| 2240 | try Int.divTrunc(&q, &r, a, b); | |
| 2241 | ||
| 2242 | testing.expect((try q.to(u32)) == 9); | |
| 2243 | testing.expect((try r.to(u32)) == 4); | |
| 2244 | } | |
| 2245 | ||
| 2246 | test "big.int div multi-single no rem" { | |
| 2247 | const op1 = 0xffffeeeeddddcccc; | |
| 2248 | const op2 = 34; | |
| 2249 | ||
| 2250 | var a = try Int.initSet(testing.allocator, op1); | |
| 2251 | defer a.deinit(); | |
| 2252 | var b = try Int.initSet(testing.allocator, op2); | |
| 2253 | defer b.deinit(); | |
| 2254 | ||
| 2255 | var q = try Int.init(testing.allocator); | |
| 2256 | defer q.deinit(); | |
| 2257 | var r = try Int.init(testing.allocator); | |
| 2258 | defer r.deinit(); | |
| 2259 | try Int.divTrunc(&q, &r, a, b); | |
| 2260 | ||
| 2261 | testing.expect((try q.to(u64)) == op1 / op2); | |
| 2262 | testing.expect((try r.to(u64)) == 0); | |
| 2263 | } | |
| 2264 | ||
| 2265 | test "big.int div multi-single with rem" { | |
| 2266 | const op1 = 0xffffeeeeddddcccf; | |
| 2267 | const op2 = 34; | |
| 2268 | ||
| 2269 | var a = try Int.initSet(testing.allocator, op1); | |
| 2270 | defer a.deinit(); | |
| 2271 | var b = try Int.initSet(testing.allocator, op2); | |
| 2272 | defer b.deinit(); | |
| 2273 | ||
| 2274 | var q = try Int.init(testing.allocator); | |
| 2275 | defer q.deinit(); | |
| 2276 | var r = try Int.init(testing.allocator); | |
| 2277 | defer r.deinit(); | |
| 2278 | try Int.divTrunc(&q, &r, a, b); | |
| 1219 | /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if | |
| 1220 | /// `|a| < |b|`, `|a| == |b|`, or `|a| > |b|` respectively. | |
| 1221 | pub fn orderAbs(a: Const, b: Const) math.Order { | |
| 1222 | if (a.limbs.len < b.limbs.len) { | |
| 1223 | return .lt; | |
| 1224 | } | |
| 1225 | if (a.limbs.len > b.limbs.len) { | |
| 1226 | return .gt; | |
| 1227 | } | |
| 2279 | 1228 | |
| 2280 | testing.expect((try q.to(u64)) == op1 / op2); | |
| 2281 | testing.expect((try r.to(u64)) == 3); | |
| 2282 | } | |
| 1229 | var i: usize = a.limbs.len - 1; | |
| 1230 | while (i != 0) : (i -= 1) { | |
| 1231 | if (a.limbs[i] != b.limbs[i]) { | |
| 1232 | break; | |
| 1233 | } | |
| 1234 | } | |
| 2283 | 1235 | |
| 2284 | test "big.int div multi>2-single" { | |
| 2285 | const op1 = 0xfefefefefefefefefefefefefefefefe; | |
| 2286 | const op2 = 0xefab8; | |
| 1236 | if (a.limbs[i] < b.limbs[i]) { | |
| 1237 | return .lt; | |
| 1238 | } else if (a.limbs[i] > b.limbs[i]) { | |
| 1239 | return .gt; | |
| 1240 | } else { | |
| 1241 | return .eq; | |
| 1242 | } | |
| 1243 | } | |
| 2287 | 1244 | |
| 2288 | var a = try Int.initSet(testing.allocator, op1); | |
| 2289 | defer a.deinit(); | |
| 2290 | var b = try Int.initSet(testing.allocator, op2); | |
| 2291 | defer b.deinit(); | |
| 1245 | /// Returns `math.Order.lt`, `math.Order.eq`, `math.Order.gt` if `a < b`, `a == b` or `a > b` respectively. | |
| 1246 | pub fn order(a: Const, b: Const) math.Order { | |
| 1247 | if (a.positive != b.positive) { | |
| 1248 | return if (a.positive) .gt else .lt; | |
| 1249 | } else { | |
| 1250 | const r = orderAbs(a, b); | |
| 1251 | return if (a.positive) r else switch (r) { | |
| 1252 | .lt => math.Order.gt, | |
| 1253 | .eq => math.Order.eq, | |
| 1254 | .gt => math.Order.lt, | |
| 1255 | }; | |
| 1256 | } | |
| 1257 | } | |
| 2292 | 1258 | |
| 2293 | var q = try Int.init(testing.allocator); | |
| 2294 | defer q.deinit(); | |
| 2295 | var r = try Int.init(testing.allocator); | |
| 2296 | defer r.deinit(); | |
| 2297 | try Int.divTrunc(&q, &r, a, b); | |
| 1259 | /// Same as `order` but the right-hand operand is a primitive integer. | |
| 1260 | pub fn orderAgainstScalar(lhs: Const, scalar: var) math.Order { | |
| 1261 | var limbs: [calcLimbLen(scalar)]Limb = undefined; | |
| 1262 | const rhs = Mutable.init(&limbs, scalar); | |
| 1263 | return order(lhs, rhs.toConst()); | |
| 1264 | } | |
| 2298 | 1265 | |
| 2299 | testing.expect((try q.to(u128)) == op1 / op2); | |
| 2300 | testing.expect((try r.to(u32)) == 0x3e4e); | |
| 2301 | } | |
| 1266 | /// Returns true if `a == 0`. | |
| 1267 | pub fn eqZero(a: Const) bool { | |
| 1268 | return a.limbs.len == 1 and a.limbs[0] == 0; | |
| 1269 | } | |
| 2302 | 1270 | |
| 2303 | test "big.int div single-single q < r" { | |
| 2304 | var a = try Int.initSet(testing.allocator, 0x0078f432); | |
| 2305 | defer a.deinit(); | |
| 2306 | var b = try Int.initSet(testing.allocator, 0x01000000); | |
| 2307 | defer b.deinit(); | |
| 1271 | /// Returns true if `|a| == |b|`. | |
| 1272 | pub fn eqAbs(a: Const, b: Const) bool { | |
| 1273 | return orderAbs(a, b) == .eq; | |
| 1274 | } | |
| 2308 | 1275 | |
| 2309 | var q = try Int.init(testing.allocator); | |
| 2310 | defer q.deinit(); | |
| 2311 | var r = try Int.init(testing.allocator); | |
| 2312 | defer r.deinit(); | |
| 2313 | try Int.divTrunc(&q, &r, a, b); | |
| 1276 | /// Returns true if `a == b`. | |
| 1277 | pub fn eq(a: Const, b: Const) bool { | |
| 1278 | return order(a, b) == .eq; | |
| 1279 | } | |
| 1280 | }; | |
| 2314 | 1281 | |
| 2315 | testing.expect((try q.to(u64)) == 0); | |
| 2316 | testing.expect((try r.to(u64)) == 0x0078f432); | |
| 2317 | } | |
| 1282 | /// An arbitrary-precision big integer along with an allocator which manages the memory. | |
| 1283 | /// | |
| 1284 | /// Memory is allocated as needed to ensure operations never overflow. The range | |
| 1285 | /// is bounded only by available memory. | |
| 1286 | pub const Managed = struct { | |
| 1287 | pub const sign_bit: usize = 1 << (usize.bit_count - 1); | |
| 2318 | 1288 | |
| 2319 | test "big.int div single-single q == r" { | |
| 2320 | var a = try Int.initSet(testing.allocator, 10); | |
| 2321 | defer a.deinit(); | |
| 2322 | var b = try Int.initSet(testing.allocator, 10); | |
| 2323 | defer b.deinit(); | |
| 1289 | /// Default number of limbs to allocate on creation of a `Managed`. | |
| 1290 | pub const default_capacity = 4; | |
| 2324 | 1291 | |
| 2325 | var q = try Int.init(testing.allocator); | |
| 2326 | defer q.deinit(); | |
| 2327 | var r = try Int.init(testing.allocator); | |
| 2328 | defer r.deinit(); | |
| 2329 | try Int.divTrunc(&q, &r, a, b); | |
| 1292 | /// Allocator used by the Managed when requesting memory. | |
| 1293 | allocator: *Allocator, | |
| 2330 | 1294 | |
| 2331 | testing.expect((try q.to(u64)) == 1); | |
| 2332 | testing.expect((try r.to(u64)) == 0); | |
| 2333 | } | |
| 1295 | /// Raw digits. These are: | |
| 1296 | /// | |
| 1297 | /// * Little-endian ordered | |
| 1298 | /// * limbs.len >= 1 | |
| 1299 | /// * Zero is represent as Managed.len() == 1 with limbs[0] == 0. | |
| 1300 | /// | |
| 1301 | /// Accessing limbs directly should be avoided. | |
| 1302 | limbs: []Limb, | |
| 2334 | 1303 | |
| 2335 | test "big.int div q=0 alias" { | |
| 2336 | var a = try Int.initSet(testing.allocator, 3); | |
| 2337 | defer a.deinit(); | |
| 2338 | var b = try Int.initSet(testing.allocator, 10); | |
| 2339 | defer b.deinit(); | |
| 1304 | /// High bit is the sign bit. If set, Managed is negative, else Managed is positive. | |
| 1305 | /// The remaining bits represent the number of limbs used by Managed. | |
| 1306 | metadata: usize, | |
| 2340 | 1307 | |
| 2341 | try Int.divTrunc(&a, &b, a, b); | |
| 1308 | /// Creates a new `Managed`. `default_capacity` limbs will be allocated immediately. | |
| 1309 | /// The integer value after initializing is `0`. | |
| 1310 | pub fn init(allocator: *Allocator) !Managed { | |
| 1311 | return initCapacity(allocator, default_capacity); | |
| 1312 | } | |
| 2342 | 1313 | |
| 2343 | testing.expect((try a.to(u64)) == 0); | |
| 2344 | testing.expect((try b.to(u64)) == 3); | |
| 2345 | } | |
| 1314 | pub fn toMutable(self: Managed) Mutable { | |
| 1315 | return .{ | |
| 1316 | .limbs = self.limbs, | |
| 1317 | .positive = self.isPositive(), | |
| 1318 | .len = self.len(), | |
| 1319 | }; | |
| 1320 | } | |
| 2346 | 1321 | |
| 2347 | test "big.int div multi-multi q < r" { | |
| 2348 | const op1 = 0x1ffffffff0078f432; | |
| 2349 | const op2 = 0x1ffffffff01000000; | |
| 2350 | var a = try Int.initSet(testing.allocator, op1); | |
| 2351 | defer a.deinit(); | |
| 2352 | var b = try Int.initSet(testing.allocator, op2); | |
| 2353 | defer b.deinit(); | |
| 2354 | ||
| 2355 | var q = try Int.init(testing.allocator); | |
| 2356 | defer q.deinit(); | |
| 2357 | var r = try Int.init(testing.allocator); | |
| 2358 | defer r.deinit(); | |
| 2359 | try Int.divTrunc(&q, &r, a, b); | |
| 2360 | ||
| 2361 | testing.expect((try q.to(u128)) == 0); | |
| 2362 | testing.expect((try r.to(u128)) == op1); | |
| 2363 | } | |
| 1322 | pub fn toConst(self: Managed) Const { | |
| 1323 | return .{ | |
| 1324 | .limbs = self.limbs[0..self.len()], | |
| 1325 | .positive = self.isPositive(), | |
| 1326 | }; | |
| 1327 | } | |
| 2364 | 1328 | |
| 2365 | test "big.int div trunc single-single +/+" { | |
| 2366 | const u: i32 = 5; | |
| 2367 | const v: i32 = 3; | |
| 1329 | /// Creates a new `Managed` with value `value`. | |
| 1330 | /// | |
| 1331 | /// This is identical to an `init`, followed by a `set`. | |
| 1332 | pub fn initSet(allocator: *Allocator, value: var) !Managed { | |
| 1333 | var s = try Managed.init(allocator); | |
| 1334 | try s.set(value); | |
| 1335 | return s; | |
| 1336 | } | |
| 2368 | 1337 | |
| 2369 | var a = try Int.initSet(testing.allocator, u); | |
| 2370 | defer a.deinit(); | |
| 2371 | var b = try Int.initSet(testing.allocator, v); | |
| 2372 | defer b.deinit(); | |
| 1338 | /// Creates a new Managed with a specific capacity. If capacity < default_capacity then the | |
| 1339 | /// default capacity will be used instead. | |
| 1340 | /// The integer value after initializing is `0`. | |
| 1341 | pub fn initCapacity(allocator: *Allocator, capacity: usize) !Managed { | |
| 1342 | return Managed{ | |
| 1343 | .allocator = allocator, | |
| 1344 | .metadata = 1, | |
| 1345 | .limbs = block: { | |
| 1346 | const limbs = try allocator.alloc(Limb, math.max(default_capacity, capacity)); | |
| 1347 | limbs[0] = 0; | |
| 1348 | break :block limbs; | |
| 1349 | }, | |
| 1350 | }; | |
| 1351 | } | |
| 2373 | 1352 | |
| 2374 | var q = try Int.init(testing.allocator); | |
| 2375 | defer q.deinit(); | |
| 2376 | var r = try Int.init(testing.allocator); | |
| 2377 | defer r.deinit(); | |
| 2378 | try Int.divTrunc(&q, &r, a, b); | |
| 1353 | /// Returns the number of limbs currently in use. | |
| 1354 | pub fn len(self: Managed) usize { | |
| 1355 | return self.metadata & ~sign_bit; | |
| 1356 | } | |
| 2379 | 1357 | |
| 2380 | // n = q * d + r | |
| 2381 | // 5 = 1 * 3 + 2 | |
| 2382 | const eq = @divTrunc(u, v); | |
| 2383 | const er = @mod(u, v); | |
| 1358 | /// Returns whether an Managed is positive. | |
| 1359 | pub fn isPositive(self: Managed) bool { | |
| 1360 | return self.metadata & sign_bit == 0; | |
| 1361 | } | |
| 2384 | 1362 | |
| 2385 | testing.expect((try q.to(i32)) == eq); | |
| 2386 | testing.expect((try r.to(i32)) == er); | |
| 2387 | } | |
| 1363 | /// Sets the sign of an Managed. | |
| 1364 | pub fn setSign(self: *Managed, positive: bool) void { | |
| 1365 | if (positive) { | |
| 1366 | self.metadata &= ~sign_bit; | |
| 1367 | } else { | |
| 1368 | self.metadata |= sign_bit; | |
| 1369 | } | |
| 1370 | } | |
| 2388 | 1371 | |
| 2389 | test "big.int div trunc single-single -/+" { | |
| 2390 | const u: i32 = -5; | |
| 2391 | const v: i32 = 3; | |
| 1372 | /// Sets the length of an Managed. | |
| 1373 | /// | |
| 1374 | /// If setLen is used, then the Managed must be normalized to suit. | |
| 1375 | pub fn setLen(self: *Managed, new_len: usize) void { | |
| 1376 | self.metadata &= sign_bit; | |
| 1377 | self.metadata |= new_len; | |
| 1378 | } | |
| 2392 | 1379 | |
| 2393 | var a = try Int.initSet(testing.allocator, u); | |
| 2394 | defer a.deinit(); | |
| 2395 | var b = try Int.initSet(testing.allocator, v); | |
| 2396 | defer b.deinit(); | |
| 1380 | pub fn setMetadata(self: *Managed, positive: bool, length: usize) void { | |
| 1381 | self.metadata = if (positive) length & ~sign_bit else length | sign_bit; | |
| 1382 | } | |
| 2397 | 1383 | |
| 2398 | var q = try Int.init(testing.allocator); | |
| 2399 | defer q.deinit(); | |
| 2400 | var r = try Int.init(testing.allocator); | |
| 2401 | defer r.deinit(); | |
| 2402 | try Int.divTrunc(&q, &r, a, b); | |
| 1384 | /// Ensures an Managed has enough space allocated for capacity limbs. If the Managed does not have | |
| 1385 | /// sufficient capacity, the exact amount will be allocated. This occurs even if the requested | |
| 1386 | /// capacity is only greater than the current capacity by one limb. | |
| 1387 | pub fn ensureCapacity(self: *Managed, capacity: usize) !void { | |
| 1388 | if (capacity <= self.limbs.len) { | |
| 1389 | return; | |
| 1390 | } | |
| 1391 | self.limbs = try self.allocator.realloc(self.limbs, capacity); | |
| 1392 | } | |
| 2403 | 1393 | |
| 2404 | // n = q * d + r | |
| 2405 | // -5 = 1 * -3 - 2 | |
| 2406 | const eq = -1; | |
| 2407 | const er = -2; | |
| 1394 | /// Frees all associated memory. | |
| 1395 | pub fn deinit(self: *Managed) void { | |
| 1396 | self.allocator.free(self.limbs); | |
| 1397 | self.* = undefined; | |
| 1398 | } | |
| 2408 | 1399 | |
| 2409 | testing.expect((try q.to(i32)) == eq); | |
| 2410 | testing.expect((try r.to(i32)) == er); | |
| 2411 | } | |
| 1400 | /// Returns a `Managed` with the same value. The returned `Managed` is a deep copy and | |
| 1401 | /// can be modified separately from the original, and its resources are managed | |
| 1402 | /// separately from the original. | |
| 1403 | pub fn clone(other: Managed) !Managed { | |
| 1404 | return other.cloneWithDifferentAllocator(other.allocator); | |
| 1405 | } | |
| 2412 | 1406 | |
| 2413 | test "big.int div trunc single-single +/-" { | |
| 2414 | const u: i32 = 5; | |
| 2415 | const v: i32 = -3; | |
| 1407 | pub fn cloneWithDifferentAllocator(other: Managed, allocator: *Allocator) !Managed { | |
| 1408 | return Managed{ | |
| 1409 | .allocator = allocator, | |
| 1410 | .metadata = other.metadata, | |
| 1411 | .limbs = block: { | |
| 1412 | var limbs = try allocator.alloc(Limb, other.len()); | |
| 1413 | mem.copy(Limb, limbs[0..], other.limbs[0..other.len()]); | |
| 1414 | break :block limbs; | |
| 1415 | }, | |
| 1416 | }; | |
| 1417 | } | |
| 2416 | 1418 | |
| 2417 | var a = try Int.initSet(testing.allocator, u); | |
| 2418 | defer a.deinit(); | |
| 2419 | var b = try Int.initSet(testing.allocator, v); | |
| 2420 | defer b.deinit(); | |
| 1419 | /// Copies the value of the integer to an existing `Managed` so that they both have the same value. | |
| 1420 | /// Extra memory will be allocated if the receiver does not have enough capacity. | |
| 1421 | pub fn copy(self: *Managed, other: Const) !void { | |
| 1422 | if (self.limbs.ptr == other.limbs.ptr) return; | |
| 2421 | 1423 | |
| 2422 | var q = try Int.init(testing.allocator); | |
| 2423 | defer q.deinit(); | |
| 2424 | var r = try Int.init(testing.allocator); | |
| 2425 | defer r.deinit(); | |
| 2426 | try Int.divTrunc(&q, &r, a, b); | |
| 1424 | try self.ensureCapacity(other.limbs.len); | |
| 1425 | mem.copy(Limb, self.limbs[0..], other.limbs[0..other.limbs.len]); | |
| 1426 | self.setMetadata(other.positive, other.limbs.len); | |
| 1427 | } | |
| 2427 | 1428 | |
| 2428 | // n = q * d + r | |
| 2429 | // 5 = -1 * -3 + 2 | |
| 2430 | const eq = -1; | |
| 2431 | const er = 2; | |
| 1429 | /// Efficiently swap a `Managed` with another. This swaps the limb pointers and a full copy is not | |
| 1430 | /// performed. The address of the limbs field will not be the same after this function. | |
| 1431 | pub fn swap(self: *Managed, other: *Managed) void { | |
| 1432 | mem.swap(Managed, self, other); | |
| 1433 | } | |
| 2432 | 1434 | |
| 2433 | testing.expect((try q.to(i32)) == eq); | |
| 2434 | testing.expect((try r.to(i32)) == er); | |
| 2435 | } | |
| 1435 | /// Debugging tool: prints the state to stderr. | |
| 1436 | pub fn dump(self: Managed) void { | |
| 1437 | for (self.limbs[0..self.len()]) |limb| { | |
| 1438 | std.debug.warn("{x} ", .{limb}); | |
| 1439 | } | |
| 1440 | std.debug.warn("capacity={} positive={}\n", .{ self.limbs.len, self.positive }); | |
| 1441 | } | |
| 2436 | 1442 | |
| 2437 | test "big.int div trunc single-single -/-" { | |
| 2438 | const u: i32 = -5; | |
| 2439 | const v: i32 = -3; | |
| 1443 | /// Negate the sign. | |
| 1444 | pub fn negate(self: *Managed) void { | |
| 1445 | self.metadata ^= sign_bit; | |
| 1446 | } | |
| 2440 | 1447 | |
| 2441 | var a = try Int.initSet(testing.allocator, u); | |
| 2442 | defer a.deinit(); | |
| 2443 | var b = try Int.initSet(testing.allocator, v); | |
| 2444 | defer b.deinit(); | |
| 1448 | /// Make positive. | |
| 1449 | pub fn abs(self: *Managed) void { | |
| 1450 | self.metadata &= ~sign_bit; | |
| 1451 | } | |
| 2445 | 1452 | |
| 2446 | var q = try Int.init(testing.allocator); | |
| 2447 | defer q.deinit(); | |
| 2448 | var r = try Int.init(testing.allocator); | |
| 2449 | defer r.deinit(); | |
| 2450 | try Int.divTrunc(&q, &r, a, b); | |
| 1453 | pub fn isOdd(self: Managed) bool { | |
| 1454 | return self.limbs[0] & 1 != 0; | |
| 1455 | } | |
| 2451 | 1456 | |
| 2452 | // n = q * d + r | |
| 2453 | // -5 = 1 * -3 - 2 | |
| 2454 | const eq = 1; | |
| 2455 | const er = -2; | |
| 1457 | pub fn isEven(self: Managed) bool { | |
| 1458 | return !self.isOdd(); | |
| 1459 | } | |
| 2456 | 1460 | |
| 2457 | testing.expect((try q.to(i32)) == eq); | |
| 2458 | testing.expect((try r.to(i32)) == er); | |
| 2459 | } | |
| 1461 | /// Returns the number of bits required to represent the absolute value of an integer. | |
| 1462 | pub fn bitCountAbs(self: Managed) usize { | |
| 1463 | return self.toConst().bitCountAbs(); | |
| 1464 | } | |
| 2460 | 1465 | |
| 2461 | test "big.int div floor single-single +/+" { | |
| 2462 | const u: i32 = 5; | |
| 2463 | const v: i32 = 3; | |
| 1466 | /// Returns the number of bits required to represent the integer in twos-complement form. | |
| 1467 | /// | |
| 1468 | /// If the integer is negative the value returned is the number of bits needed by a signed | |
| 1469 | /// integer to represent the value. If positive the value is the number of bits for an | |
| 1470 | /// unsigned integer. Any unsigned integer will fit in the signed integer with bitcount | |
| 1471 | /// one greater than the returned value. | |
| 1472 | /// | |
| 1473 | /// e.g. -127 returns 8 as it will fit in an i8. 127 returns 7 since it fits in a u7. | |
| 1474 | pub fn bitCountTwosComp(self: Managed) usize { | |
| 1475 | return self.toConst().bitCountTwosComp(); | |
| 1476 | } | |
| 2464 | 1477 | |
| 2465 | var a = try Int.initSet(testing.allocator, u); | |
| 2466 | defer a.deinit(); | |
| 2467 | var b = try Int.initSet(testing.allocator, v); | |
| 2468 | defer b.deinit(); | |
| 1478 | pub fn fitsInTwosComp(self: Managed, is_signed: bool, bit_count: usize) bool { | |
| 1479 | return self.toConst().fitsInTwosComp(is_signed, bit_count); | |
| 1480 | } | |
| 2469 | 1481 | |
| 2470 | var q = try Int.init(testing.allocator); | |
| 2471 | defer q.deinit(); | |
| 2472 | var r = try Int.init(testing.allocator); | |
| 2473 | defer r.deinit(); | |
| 2474 | try Int.divFloor(&q, &r, a, b); | |
| 1482 | /// Returns whether self can fit into an integer of the requested type. | |
| 1483 | pub fn fits(self: Managed, comptime T: type) bool { | |
| 1484 | return self.toConst().fits(T); | |
| 1485 | } | |
| 2475 | 1486 | |
| 2476 | // n = q * d + r | |
| 2477 | // 5 = 1 * 3 + 2 | |
| 2478 | const eq = 1; | |
| 2479 | const er = 2; | |
| 1487 | /// Returns the approximate size of the integer in the given base. Negative values accommodate for | |
| 1488 | /// the minus sign. This is used for determining the number of characters needed to print the | |
| 1489 | /// value. It is inexact and may exceed the given value by ~1-2 bytes. | |
| 1490 | pub fn sizeInBaseUpperBound(self: Managed, base: usize) usize { | |
| 1491 | return self.toConst().sizeInBaseUpperBound(base); | |
| 1492 | } | |
| 2480 | 1493 | |
| 2481 | testing.expect((try q.to(i32)) == eq); | |
| 2482 | testing.expect((try r.to(i32)) == er); | |
| 2483 | } | |
| 1494 | /// Sets an Managed to value. Value must be an primitive integer type. | |
| 1495 | pub fn set(self: *Managed, value: var) Allocator.Error!void { | |
| 1496 | try self.ensureCapacity(calcLimbLen(value)); | |
| 1497 | var m = self.toMutable(); | |
| 1498 | m.set(value); | |
| 1499 | self.setMetadata(m.positive, m.len); | |
| 1500 | } | |
| 2484 | 1501 | |
| 2485 | test "big.int div floor single-single -/+" { | |
| 2486 | const u: i32 = -5; | |
| 2487 | const v: i32 = 3; | |
| 1502 | pub const ConvertError = Const.ConvertError; | |
| 2488 | 1503 | |
| 2489 | var a = try Int.initSet(testing.allocator, u); | |
| 2490 | defer a.deinit(); | |
| 2491 | var b = try Int.initSet(testing.allocator, v); | |
| 2492 | defer b.deinit(); | |
| 1504 | /// Convert self to type T. | |
| 1505 | /// | |
| 1506 | /// Returns an error if self cannot be narrowed into the requested type without truncation. | |
| 1507 | pub fn to(self: Managed, comptime T: type) ConvertError!T { | |
| 1508 | return self.toConst().to(T); | |
| 1509 | } | |
| 2493 | 1510 | |
| 2494 | var q = try Int.init(testing.allocator); | |
| 2495 | defer q.deinit(); | |
| 2496 | var r = try Int.init(testing.allocator); | |
| 2497 | defer r.deinit(); | |
| 2498 | try Int.divFloor(&q, &r, a, b); | |
| 1511 | /// Set self from the string representation `value`. | |
| 1512 | /// | |
| 1513 | /// `value` must contain only digits <= `base` and is case insensitive. Base prefixes are | |
| 1514 | /// not allowed (e.g. 0x43 should simply be 43). Underscores in the input string are | |
| 1515 | /// ignored and can be used as digit separators. | |
| 1516 | /// | |
| 1517 | /// Returns an error if memory could not be allocated or `value` has invalid digits for the | |
| 1518 | /// requested base. | |
| 1519 | /// | |
| 1520 | /// self's allocator is used for temporary storage to boost multiplication performance. | |
| 1521 | pub fn setString(self: *Managed, base: u8, value: []const u8) !void { | |
| 1522 | if (base < 2 or base > 16) return error.InvalidBase; | |
| 1523 | const den = (@sizeOf(Limb) * 8 / base); | |
| 1524 | try self.ensureCapacity((value.len + (den - 1)) / den); | |
| 1525 | const limbs_buffer = try self.allocator.alloc(Limb, calcSetStringLimbsBufferLen(base, value.len)); | |
| 1526 | defer self.allocator.free(limbs_buffer); | |
| 1527 | var m = self.toMutable(); | |
| 1528 | try m.setString(base, value, limbs_buffer, self.allocator); | |
| 1529 | self.setMetadata(m.positive, m.len); | |
| 1530 | } | |
| 2499 | 1531 | |
| 2500 | // n = q * d + r | |
| 2501 | // -5 = -2 * 3 + 1 | |
| 2502 | const eq = -2; | |
| 2503 | const er = 1; | |
| 1532 | /// Converts self to a string in the requested base. Memory is allocated from the provided | |
| 1533 | /// allocator and not the one present in self. | |
| 1534 | pub fn toString(self: Managed, allocator: *Allocator, base: u8, uppercase: bool) ![]u8 { | |
| 1535 | if (base < 2 or base > 16) return error.InvalidBase; | |
| 1536 | return self.toConst().toStringAlloc(self.allocator, base, uppercase); | |
| 1537 | } | |
| 2504 | 1538 | |
| 2505 | testing.expect((try q.to(i32)) == eq); | |
| 2506 | testing.expect((try r.to(i32)) == er); | |
| 2507 | } | |
| 1539 | /// To allow `std.fmt.format` to work with `Managed`. | |
| 1540 | /// If the integer is larger than `pow(2, 64 * @sizeOf(usize) * 8), this function will fail | |
| 1541 | /// to print the string, printing "(BigInt)" instead of a number. | |
| 1542 | /// This is because the rendering algorithm requires reversing a string, which requires O(N) memory. | |
| 1543 | /// See `toString` and `toStringAlloc` for a way to print big integers without failure. | |
| 1544 | pub fn format( | |
| 1545 | self: Managed, | |
| 1546 | comptime fmt: []const u8, | |
| 1547 | options: std.fmt.FormatOptions, | |
| 1548 | out_stream: var, | |
| 1549 | ) !void { | |
| 1550 | return self.toConst().format(fmt, options, out_stream); | |
| 1551 | } | |
| 2508 | 1552 | |
| 2509 | test "big.int div floor single-single +/-" { | |
| 2510 | const u: i32 = 5; | |
| 2511 | const v: i32 = -3; | |
| 1553 | /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| == | |
| 1554 | /// |b| or |a| > |b| respectively. | |
| 1555 | pub fn orderAbs(a: Managed, b: Managed) math.Order { | |
| 1556 | return a.toConst().orderAbs(b.toConst()); | |
| 1557 | } | |
| 2512 | 1558 | |
| 2513 | var a = try Int.initSet(testing.allocator, u); | |
| 2514 | defer a.deinit(); | |
| 2515 | var b = try Int.initSet(testing.allocator, v); | |
| 2516 | defer b.deinit(); | |
| 1559 | /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a | |
| 1560 | /// > b respectively. | |
| 1561 | pub fn order(a: Managed, b: Managed) math.Order { | |
| 1562 | return a.toConst().order(b.toConst()); | |
| 1563 | } | |
| 2517 | 1564 | |
| 2518 | var q = try Int.init(testing.allocator); | |
| 2519 | defer q.deinit(); | |
| 2520 | var r = try Int.init(testing.allocator); | |
| 2521 | defer r.deinit(); | |
| 2522 | try Int.divFloor(&q, &r, a, b); | |
| 1565 | /// Returns true if a == 0. | |
| 1566 | pub fn eqZero(a: Managed) bool { | |
| 1567 | return a.toConst().eqZero(); | |
| 1568 | } | |
| 2523 | 1569 | |
| 2524 | // n = q * d + r | |
| 2525 | // 5 = -2 * -3 - 1 | |
| 2526 | const eq = -2; | |
| 2527 | const er = -1; | |
| 1570 | /// Returns true if |a| == |b|. | |
| 1571 | pub fn eqAbs(a: Managed, b: Managed) bool { | |
| 1572 | return a.toConst().eqAbs(b.toConst()); | |
| 1573 | } | |
| 2528 | 1574 | |
| 2529 | testing.expect((try q.to(i32)) == eq); | |
| 2530 | testing.expect((try r.to(i32)) == er); | |
| 2531 | } | |
| 1575 | /// Returns true if a == b. | |
| 1576 | pub fn eq(a: Managed, b: Managed) bool { | |
| 1577 | return a.toConst().eq(b.toConst()); | |
| 1578 | } | |
| 2532 | 1579 | |
| 2533 | test "big.int div floor single-single -/-" { | |
| 2534 | const u: i32 = -5; | |
| 2535 | const v: i32 = -3; | |
| 1580 | /// Normalize a possible sequence of leading zeros. | |
| 1581 | /// | |
| 1582 | /// [1, 2, 3, 4, 0] -> [1, 2, 3, 4] | |
| 1583 | /// [1, 2, 0, 0, 0] -> [1, 2] | |
| 1584 | /// [0, 0, 0, 0, 0] -> [0] | |
| 1585 | pub fn normalize(r: *Managed, length: usize) void { | |
| 1586 | assert(length > 0); | |
| 1587 | assert(length <= r.limbs.len); | |
| 2536 | 1588 | |
| 2537 | var a = try Int.initSet(testing.allocator, u); | |
| 2538 | defer a.deinit(); | |
| 2539 | var b = try Int.initSet(testing.allocator, v); | |
| 2540 | defer b.deinit(); | |
| 1589 | var j = length; | |
| 1590 | while (j > 0) : (j -= 1) { | |
| 1591 | if (r.limbs[j - 1] != 0) { | |
| 1592 | break; | |
| 1593 | } | |
| 1594 | } | |
| 2541 | 1595 | |
| 2542 | var q = try Int.init(testing.allocator); | |
| 2543 | defer q.deinit(); | |
| 2544 | var r = try Int.init(testing.allocator); | |
| 2545 | defer r.deinit(); | |
| 2546 | try Int.divFloor(&q, &r, a, b); | |
| 1596 | // Handle zero | |
| 1597 | r.setLen(if (j != 0) j else 1); | |
| 1598 | } | |
| 2547 | 1599 | |
| 2548 | // n = q * d + r | |
| 2549 | // -5 = 2 * -3 + 1 | |
| 2550 | const eq = 1; | |
| 2551 | const er = -2; | |
| 1600 | /// r = a + scalar | |
| 1601 | /// | |
| 1602 | /// r and a may be aliases. | |
| 1603 | /// scalar is a primitive integer type. | |
| 1604 | /// | |
| 1605 | /// Returns an error if memory could not be allocated. | |
| 1606 | pub fn addScalar(r: *Managed, a: Const, scalar: var) Allocator.Error!void { | |
| 1607 | try r.ensureCapacity(math.max(a.limbs.len, calcLimbLen(scalar)) + 1); | |
| 1608 | var m = r.toMutable(); | |
| 1609 | m.addScalar(a, scalar); | |
| 1610 | r.setMetadata(m.positive, m.len); | |
| 1611 | } | |
| 2552 | 1612 | |
| 2553 | testing.expect((try q.to(i32)) == eq); | |
| 2554 | testing.expect((try r.to(i32)) == er); | |
| 2555 | } | |
| 1613 | /// r = a + b | |
| 1614 | /// | |
| 1615 | /// r, a and b may be aliases. | |
| 1616 | /// | |
| 1617 | /// Returns an error if memory could not be allocated. | |
| 1618 | pub fn add(r: *Managed, a: Const, b: Const) Allocator.Error!void { | |
| 1619 | try r.ensureCapacity(math.max(a.limbs.len, b.limbs.len) + 1); | |
| 1620 | var m = r.toMutable(); | |
| 1621 | m.add(a, b); | |
| 1622 | r.setMetadata(m.positive, m.len); | |
| 1623 | } | |
| 2556 | 1624 | |
| 2557 | test "big.int div multi-multi with rem" { | |
| 2558 | var a = try Int.initSet(testing.allocator, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999); | |
| 2559 | defer a.deinit(); | |
| 2560 | var b = try Int.initSet(testing.allocator, 0x99990000111122223333); | |
| 2561 | defer b.deinit(); | |
| 1625 | /// r = a - b | |
| 1626 | /// | |
| 1627 | /// r, a and b may be aliases. | |
| 1628 | /// | |
| 1629 | /// Returns an error if memory could not be allocated. | |
| 1630 | pub fn sub(r: *Managed, a: Const, b: Const) !void { | |
| 1631 | try r.ensureCapacity(math.max(a.limbs.len, b.limbs.len) + 1); | |
| 1632 | var m = r.toMutable(); | |
| 1633 | m.sub(a, b); | |
| 1634 | r.setMetadata(m.positive, m.len); | |
| 1635 | } | |
| 2562 | 1636 | |
| 2563 | var q = try Int.init(testing.allocator); | |
| 2564 | defer q.deinit(); | |
| 2565 | var r = try Int.init(testing.allocator); | |
| 2566 | defer r.deinit(); | |
| 2567 | try Int.divTrunc(&q, &r, a, b); | |
| 1637 | /// rma = a * b | |
| 1638 | /// | |
| 1639 | /// rma, a and b may be aliases. However, it is more efficient if rma does not alias a or b. | |
| 1640 | /// | |
| 1641 | /// Returns an error if memory could not be allocated. | |
| 1642 | /// | |
| 1643 | /// rma's allocator is used for temporary storage to speed up the multiplication. | |
| 1644 | pub fn mul(rma: *Managed, a: Const, b: Const) !void { | |
| 1645 | try rma.ensureCapacity(a.limbs.len + b.limbs.len + 1); | |
| 1646 | var alias_count: usize = 0; | |
| 1647 | if (rma.limbs.ptr == a.limbs.ptr) | |
| 1648 | alias_count += 1; | |
| 1649 | if (rma.limbs.ptr == b.limbs.ptr) | |
| 1650 | alias_count += 1; | |
| 1651 | var m = rma.toMutable(); | |
| 1652 | if (alias_count == 0) { | |
| 1653 | m.mulNoAlias(a, b, rma.allocator); | |
| 1654 | } else { | |
| 1655 | const limb_count = calcMulLimbsBufferLen(a.limbs.len, b.limbs.len, alias_count); | |
| 1656 | const limbs_buffer = try rma.allocator.alloc(Limb, limb_count); | |
| 1657 | defer rma.allocator.free(limbs_buffer); | |
| 1658 | m.mul(a, b, limbs_buffer, rma.allocator); | |
| 1659 | } | |
| 1660 | rma.setMetadata(m.positive, m.len); | |
| 1661 | } | |
| 2568 | 1662 | |
| 2569 | testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); | |
| 2570 | testing.expect((try r.to(u128)) == 0x28de0acacd806823638); | |
| 2571 | } | |
| 1663 | /// q = a / b (rem r) | |
| 1664 | /// | |
| 1665 | /// a / b are floored (rounded towards 0). | |
| 1666 | /// | |
| 1667 | /// Returns an error if memory could not be allocated. | |
| 1668 | /// | |
| 1669 | /// q's allocator is used for temporary storage to speed up the multiplication. | |
| 1670 | pub fn divFloor(q: *Managed, r: *Managed, a: Const, b: Const) !void { | |
| 1671 | try q.ensureCapacity(a.limbs.len + b.limbs.len + 1); | |
| 1672 | try r.ensureCapacity(a.limbs.len); | |
| 1673 | var mq = q.toMutable(); | |
| 1674 | var mr = r.toMutable(); | |
| 1675 | const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.limbs.len, b.limbs.len)); | |
| 1676 | defer q.allocator.free(limbs_buffer); | |
| 1677 | mq.divFloor(&mr, a, b, limbs_buffer, q.allocator); | |
| 1678 | q.setMetadata(mq.positive, mq.len); | |
| 1679 | r.setMetadata(mr.positive, mr.len); | |
| 1680 | } | |
| 2572 | 1681 | |
| 2573 | test "big.int div multi-multi no rem" { | |
| 2574 | var a = try Int.initSet(testing.allocator, 0x8888999911110000ffffeeeedb4fec200ee3a4286361); | |
| 2575 | defer a.deinit(); | |
| 2576 | var b = try Int.initSet(testing.allocator, 0x99990000111122223333); | |
| 2577 | defer b.deinit(); | |
| 1682 | /// q = a / b (rem r) | |
| 1683 | /// | |
| 1684 | /// a / b are truncated (rounded towards -inf). | |
| 1685 | /// | |
| 1686 | /// Returns an error if memory could not be allocated. | |
| 1687 | /// | |
| 1688 | /// q's allocator is used for temporary storage to speed up the multiplication. | |
| 1689 | pub fn divTrunc(q: *Managed, r: *Managed, a: Const, b: Const) !void { | |
| 1690 | try q.ensureCapacity(a.limbs.len + b.limbs.len + 1); | |
| 1691 | try r.ensureCapacity(a.limbs.len); | |
| 1692 | var mq = q.toMutable(); | |
| 1693 | var mr = r.toMutable(); | |
| 1694 | const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.limbs.len, b.limbs.len)); | |
| 1695 | defer q.allocator.free(limbs_buffer); | |
| 1696 | mq.divTrunc(&mr, a, b, limbs_buffer, q.allocator); | |
| 1697 | q.setMetadata(mq.positive, mq.len); | |
| 1698 | r.setMetadata(mr.positive, mr.len); | |
| 1699 | } | |
| 2578 | 1700 | |
| 2579 | var q = try Int.init(testing.allocator); | |
| 2580 | defer q.deinit(); | |
| 2581 | var r = try Int.init(testing.allocator); | |
| 2582 | defer r.deinit(); | |
| 2583 | try Int.divTrunc(&q, &r, a, b); | |
| 1701 | /// r = a << shift, in other words, r = a * 2^shift | |
| 1702 | pub fn shiftLeft(r: *Managed, a: Managed, shift: usize) !void { | |
| 1703 | try r.ensureCapacity(a.len() + (shift / Limb.bit_count) + 1); | |
| 1704 | var m = r.toMutable(); | |
| 1705 | m.shiftLeft(a.toConst(), shift); | |
| 1706 | r.setMetadata(m.positive, m.len); | |
| 1707 | } | |
| 2584 | 1708 | |
| 2585 | testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); | |
| 2586 | testing.expect((try r.to(u128)) == 0); | |
| 2587 | } | |
| 1709 | /// r = a >> shift | |
| 1710 | pub fn shiftRight(r: *Managed, a: Managed, shift: usize) !void { | |
| 1711 | if (a.len() <= shift / Limb.bit_count) { | |
| 1712 | r.metadata = 1; | |
| 1713 | r.limbs[0] = 0; | |
| 1714 | return; | |
| 1715 | } | |
| 2588 | 1716 | |
| 2589 | test "big.int div multi-multi (2 branch)" { | |
| 2590 | var a = try Int.initSet(testing.allocator, 0x866666665555555588888887777777761111111111111111); | |
| 2591 | defer a.deinit(); | |
| 2592 | var b = try Int.initSet(testing.allocator, 0x86666666555555554444444433333333); | |
| 2593 | defer b.deinit(); | |
| 1717 | try r.ensureCapacity(a.len() - (shift / Limb.bit_count)); | |
| 1718 | var m = r.toMutable(); | |
| 1719 | m.shiftRight(a.toConst(), shift); | |
| 1720 | r.setMetadata(m.positive, m.len); | |
| 1721 | } | |
| 2594 | 1722 | |
| 2595 | var q = try Int.init(testing.allocator); | |
| 2596 | defer q.deinit(); | |
| 2597 | var r = try Int.init(testing.allocator); | |
| 2598 | defer r.deinit(); | |
| 2599 | try Int.divTrunc(&q, &r, a, b); | |
| 1723 | /// r = a | b | |
| 1724 | /// | |
| 1725 | /// a and b are zero-extended to the longer of a or b. | |
| 1726 | pub fn bitOr(r: *Managed, a: Managed, b: Managed) !void { | |
| 1727 | try r.ensureCapacity(math.max(a.len(), b.len())); | |
| 1728 | var m = r.toMutable(); | |
| 1729 | m.bitOr(a.toConst(), b.toConst()); | |
| 1730 | r.setMetadata(m.positive, m.len); | |
| 1731 | } | |
| 2600 | 1732 | |
| 2601 | testing.expect((try q.to(u128)) == 0x10000000000000000); | |
| 2602 | testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111); | |
| 2603 | } | |
| 1733 | /// r = a & b | |
| 1734 | pub fn bitAnd(r: *Managed, a: Managed, b: Managed) !void { | |
| 1735 | try r.ensureCapacity(math.min(a.len(), b.len())); | |
| 1736 | var m = r.toMutable(); | |
| 1737 | m.bitAnd(a.toConst(), b.toConst()); | |
| 1738 | r.setMetadata(m.positive, m.len); | |
| 1739 | } | |
| 2604 | 1740 | |
| 2605 | test "big.int div multi-multi (3.1/3.3 branch)" { | |
| 2606 | var a = try Int.initSet(testing.allocator, 0x11111111111111111111111111111111111111111111111111111111111111); | |
| 2607 | defer a.deinit(); | |
| 2608 | var b = try Int.initSet(testing.allocator, 0x1111111111111111111111111111111111111111171); | |
| 2609 | defer b.deinit(); | |
| 1741 | /// r = a ^ b | |
| 1742 | pub fn bitXor(r: *Managed, a: Managed, b: Managed) !void { | |
| 1743 | try r.ensureCapacity(math.max(a.len(), b.len())); | |
| 1744 | var m = r.toMutable(); | |
| 1745 | m.bitXor(a.toConst(), b.toConst()); | |
| 1746 | r.setMetadata(m.positive, m.len); | |
| 1747 | } | |
| 2610 | 1748 | |
| 2611 | var q = try Int.init(testing.allocator); | |
| 2612 | defer q.deinit(); | |
| 2613 | var r = try Int.init(testing.allocator); | |
| 2614 | defer r.deinit(); | |
| 2615 | try Int.divTrunc(&q, &r, a, b); | |
| 1749 | /// rma may alias x or y. | |
| 1750 | /// x and y may alias each other. | |
| 1751 | /// | |
| 1752 | /// rma's allocator is used for temporary storage to boost multiplication performance. | |
| 1753 | pub fn gcd(rma: *Managed, x: Managed, y: Managed) !void { | |
| 1754 | try rma.ensureCapacity(math.min(x.len(), y.len())); | |
| 1755 | var m = rma.toMutable(); | |
| 1756 | var limbs_buffer = std.ArrayList(Limb).init(rma.allocator); | |
| 1757 | defer limbs_buffer.deinit(); | |
| 1758 | try m.gcd(x.toConst(), y.toConst(), &limbs_buffer); | |
| 1759 | rma.setMetadata(m.positive, m.len); | |
| 1760 | } | |
| 1761 | }; | |
| 2616 | 1762 | |
| 2617 | testing.expect((try q.to(u128)) == 0xfffffffffffffffffff); | |
| 2618 | testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282); | |
| 2619 | } | |
| 1763 | /// Knuth 4.3.1, Algorithm M. | |
| 1764 | /// | |
| 1765 | /// r MUST NOT alias any of a or b. | |
| 1766 | fn llmulacc(opt_allocator: ?*Allocator, r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 1767 | @setRuntimeSafety(false); | |
| 1768 | ||
| 1769 | const a_norm = a[0..llnormalize(a)]; | |
| 1770 | const b_norm = b[0..llnormalize(b)]; | |
| 1771 | var x = a_norm; | |
| 1772 | var y = b_norm; | |
| 1773 | if (a_norm.len > b_norm.len) { | |
| 1774 | x = b_norm; | |
| 1775 | y = a_norm; | |
| 1776 | } | |
| 1777 | ||
| 1778 | assert(r.len >= x.len + y.len + 1); | |
| 1779 | ||
| 1780 | // 48 is a pretty abitrary size chosen based on performance of a factorial program. | |
| 1781 | if (x.len > 48) { | |
| 1782 | if (opt_allocator) |allocator| { | |
| 1783 | llmulacc_karatsuba(allocator, r, x, y) catch |err| switch (err) { | |
| 1784 | error.OutOfMemory => {}, // handled below | |
| 1785 | }; | |
| 1786 | } | |
| 1787 | } | |
| 2620 | 1788 | |
| 2621 | test "big.int div multi-single zero-limb trailing" { | |
| 2622 | var a = try Int.initSet(testing.allocator, 0x60000000000000000000000000000000000000000000000000000000000000000); | |
| 2623 | defer a.deinit(); | |
| 2624 | var b = try Int.initSet(testing.allocator, 0x10000000000000000); | |
| 2625 | defer b.deinit(); | |
| 2626 | ||
| 2627 | var q = try Int.init(testing.allocator); | |
| 2628 | defer q.deinit(); | |
| 2629 | var r = try Int.init(testing.allocator); | |
| 2630 | defer r.deinit(); | |
| 2631 | try Int.divTrunc(&q, &r, a, b); | |
| 2632 | ||
| 2633 | var expected = try Int.initSet(testing.allocator, 0x6000000000000000000000000000000000000000000000000); | |
| 2634 | defer expected.deinit(); | |
| 2635 | testing.expect(q.eq(expected)); | |
| 2636 | testing.expect(r.eqZero()); | |
| 1789 | // Basecase multiplication | |
| 1790 | var i: usize = 0; | |
| 1791 | while (i < x.len) : (i += 1) { | |
| 1792 | llmulDigit(r[i..], y, x[i]); | |
| 1793 | } | |
| 2637 | 1794 | } |
| 2638 | 1795 | |
| 2639 | test "big.int div multi-multi zero-limb trailing (with rem)" { | |
| 2640 | var a = try Int.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000); | |
| 2641 | defer a.deinit(); | |
| 2642 | var b = try Int.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000); | |
| 2643 | defer b.deinit(); | |
| 2644 | ||
| 2645 | var q = try Int.init(testing.allocator); | |
| 2646 | defer q.deinit(); | |
| 2647 | var r = try Int.init(testing.allocator); | |
| 2648 | defer r.deinit(); | |
| 2649 | try Int.divTrunc(&q, &r, a, b); | |
| 2650 | ||
| 2651 | testing.expect((try q.to(u128)) == 0x10000000000000000); | |
| 1796 | /// Knuth 4.3.1, Algorithm M. | |
| 1797 | /// | |
| 1798 | /// r MUST NOT alias any of a or b. | |
| 1799 | fn llmulacc_karatsuba(allocator: *Allocator, r: []Limb, x: []const Limb, y: []const Limb) error{OutOfMemory}!void { | |
| 1800 | @setRuntimeSafety(false); | |
| 2652 | 1801 | |
| 2653 | const rs = try r.toString(testing.allocator, 16, false); | |
| 2654 | defer testing.allocator.free(rs); | |
| 2655 | testing.expect(std.mem.eql(u8, rs, "4444444344444443111111111111111100000000000000000000000000000000")); | |
| 2656 | } | |
| 1802 | assert(r.len >= x.len + y.len + 1); | |
| 2657 | 1803 | |
| 2658 | test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count > divisor zero-limb count" { | |
| 2659 | var a = try Int.initSet(testing.allocator, 0x8666666655555555888888877777777611111111111111110000000000000000); | |
| 2660 | defer a.deinit(); | |
| 2661 | var b = try Int.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000); | |
| 2662 | defer b.deinit(); | |
| 1804 | const split = @divFloor(x.len, 2); | |
| 1805 | var x0 = x[0..split]; | |
| 1806 | var x1 = x[split..x.len]; | |
| 1807 | var y0 = y[0..split]; | |
| 1808 | var y1 = y[split..y.len]; | |
| 2663 | 1809 | |
| 2664 | var q = try Int.init(testing.allocator); | |
| 2665 | defer q.deinit(); | |
| 2666 | var r = try Int.init(testing.allocator); | |
| 2667 | defer r.deinit(); | |
| 2668 | try Int.divTrunc(&q, &r, a, b); | |
| 1810 | var tmp = try allocator.alloc(Limb, x1.len + y1.len + 1); | |
| 1811 | defer allocator.free(tmp); | |
| 1812 | mem.set(Limb, tmp, 0); | |
| 2669 | 1813 | |
| 2670 | testing.expect((try q.to(u128)) == 0x1); | |
| 1814 | llmulacc(allocator, tmp, x1, y1); | |
| 2671 | 1815 | |
| 2672 | const rs = try r.toString(testing.allocator, 16, false); | |
| 2673 | defer testing.allocator.free(rs); | |
| 2674 | testing.expect(std.mem.eql(u8, rs, "444444434444444311111111111111110000000000000000")); | |
| 2675 | } | |
| 1816 | var length = llnormalize(tmp); | |
| 1817 | _ = llaccum(r[split..], tmp[0..length]); | |
| 1818 | _ = llaccum(r[split * 2 ..], tmp[0..length]); | |
| 2676 | 1819 | |
| 2677 | test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count < divisor zero-limb count" { | |
| 2678 | var a = try Int.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000); | |
| 2679 | defer a.deinit(); | |
| 2680 | var b = try Int.initSet(testing.allocator, 0x866666665555555544444444333333330000000000000000); | |
| 2681 | defer b.deinit(); | |
| 2682 | ||
| 2683 | var q = try Int.init(testing.allocator); | |
| 2684 | defer q.deinit(); | |
| 2685 | var r = try Int.init(testing.allocator); | |
| 2686 | defer r.deinit(); | |
| 2687 | try Int.divTrunc(&q, &r, a, b); | |
| 2688 | ||
| 2689 | const qs = try q.toString(testing.allocator, 16, false); | |
| 2690 | defer testing.allocator.free(qs); | |
| 2691 | testing.expect(std.mem.eql(u8, qs, "10000000000000000820820803105186f")); | |
| 2692 | ||
| 2693 | const rs = try r.toString(testing.allocator, 16, false); | |
| 2694 | defer testing.allocator.free(rs); | |
| 2695 | testing.expect(std.mem.eql(u8, rs, "4e11f2baa5896a321d463b543d0104e30000000000000000")); | |
| 2696 | } | |
| 1820 | mem.set(Limb, tmp[0..length], 0); | |
| 2697 | 1821 | |
| 2698 | test "big.int div multi-multi fuzz case #1" { | |
| 2699 | var a = try Int.init(testing.allocator); | |
| 2700 | defer a.deinit(); | |
| 2701 | var b = try Int.init(testing.allocator); | |
| 2702 | defer b.deinit(); | |
| 1822 | llmulacc(allocator, tmp, x0, y0); | |
| 2703 | 1823 | |
| 2704 | try a.setString(16, "ffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); | |
| 2705 | try b.setString(16, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffc000000000000000000000000000000007fffffffffff"); | |
| 1824 | length = llnormalize(tmp); | |
| 1825 | _ = llaccum(r[0..], tmp[0..length]); | |
| 1826 | _ = llaccum(r[split..], tmp[0..length]); | |
| 2706 | 1827 | |
| 2707 | var q = try Int.init(testing.allocator); | |
| 2708 | defer q.deinit(); | |
| 2709 | var r = try Int.init(testing.allocator); | |
| 2710 | defer r.deinit(); | |
| 2711 | try Int.divTrunc(&q, &r, a, b); | |
| 1828 | const x_cmp = llcmp(x1, x0); | |
| 1829 | const y_cmp = llcmp(y1, y0); | |
| 1830 | if (x_cmp * y_cmp == 0) { | |
| 1831 | return; | |
| 1832 | } | |
| 1833 | const x0_len = llnormalize(x0); | |
| 1834 | const x1_len = llnormalize(x1); | |
| 1835 | var j0 = try allocator.alloc(Limb, math.max(x0_len, x1_len)); | |
| 1836 | defer allocator.free(j0); | |
| 1837 | if (x_cmp == 1) { | |
| 1838 | llsub(j0, x1[0..x1_len], x0[0..x0_len]); | |
| 1839 | } else { | |
| 1840 | llsub(j0, x0[0..x0_len], x1[0..x1_len]); | |
| 1841 | } | |
| 2712 | 1842 | |
| 2713 | const qs = try q.toString(testing.allocator, 16, false); | |
| 2714 | defer testing.allocator.free(qs); | |
| 2715 | testing.expect(std.mem.eql(u8, qs, "3ffffffffffffffffffffffffffff0000000000000000000000000000000000001ffffffffffffffffffffffffffff7fffffffe000000000000000000000000000180000000000000000000003fffffbfffffffdfffffffffffffeffff800000100101000000100000000020003fffffdfbfffffe3ffffffffffffeffff7fffc00800a100000017ffe000002000400007efbfff7fe9f00000037ffff3fff7fffa004006100000009ffe00000190038200bf7d2ff7fefe80400060000f7d7f8fbf9401fe38e0403ffc0bdffffa51102c300d7be5ef9df4e5060007b0127ad3fa69f97d0f820b6605ff617ddf7f32ad7a05c0d03f2e7bc78a6000e087a8bbcdc59e07a5a079128a7861f553ddebed7e8e56701756f9ead39b48cd1b0831889ea6ec1fddf643d0565b075ff07e6caea4e2854ec9227fd635ed60a2f5eef2893052ffd54718fa08604acbf6a15e78a467c4a3c53c0278af06c4416573f925491b195e8fd79302cb1aaf7caf4ecfc9aec1254cc969786363ac729f914c6ddcc26738d6b0facd54eba026580aba2eb6482a088b0d224a8852420b91ec1")); | |
| 1843 | const y0_len = llnormalize(y0); | |
| 1844 | const y1_len = llnormalize(y1); | |
| 1845 | var j1 = try allocator.alloc(Limb, math.max(y0_len, y1_len)); | |
| 1846 | defer allocator.free(j1); | |
| 1847 | if (y_cmp == 1) { | |
| 1848 | llsub(j1, y1[0..y1_len], y0[0..y0_len]); | |
| 1849 | } else { | |
| 1850 | llsub(j1, y0[0..y0_len], y1[0..y1_len]); | |
| 1851 | } | |
| 1852 | const j0_len = llnormalize(j0); | |
| 1853 | const j1_len = llnormalize(j1); | |
| 1854 | if (x_cmp == y_cmp) { | |
| 1855 | mem.set(Limb, tmp[0..length], 0); | |
| 1856 | llmulacc(allocator, tmp, j0, j1); | |
| 2716 | 1857 | |
| 2717 | const rs = try r.toString(testing.allocator, 16, false); | |
| 2718 | defer testing.allocator.free(rs); | |
| 2719 | testing.expect(std.mem.eql(u8, rs, "310d1d4c414426b4836c2635bad1df3a424e50cbdd167ffccb4dfff57d36b4aae0d6ca0910698220171a0f3373c1060a046c2812f0027e321f72979daa5e7973214170d49e885de0c0ecc167837d44502430674a82522e5df6a0759548052420b91ec1")); | |
| 1858 | length = llnormalize(tmp); | |
| 1859 | llsub(r[split..], r[split..], tmp[0..length]); | |
| 1860 | } else { | |
| 1861 | llmulacc(allocator, r[split..], j0, j1); | |
| 1862 | } | |
| 2720 | 1863 | } |
| 2721 | 1864 | |
| 2722 | test "big.int div multi-multi fuzz case #2" { | |
| 2723 | var a = try Int.init(testing.allocator); | |
| 2724 | defer a.deinit(); | |
| 2725 | var b = try Int.init(testing.allocator); | |
| 2726 | defer b.deinit(); | |
| 1865 | // r = r + a | |
| 1866 | fn llaccum(r: []Limb, a: []const Limb) Limb { | |
| 1867 | @setRuntimeSafety(false); | |
| 1868 | assert(r.len != 0 and a.len != 0); | |
| 1869 | assert(r.len >= a.len); | |
| 2727 | 1870 | |
| 2728 | try a.setString(16, "3ffffffffe00000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000001fffffffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffc000000000000000000000000000000000000000000000000000000000000000"); | |
| 2729 | try b.setString(16, "ffc0000000000000000000000000000000000000000000000000"); | |
| 1871 | var i: usize = 0; | |
| 1872 | var carry: Limb = 0; | |
| 2730 | 1873 | |
| 2731 | var q = try Int.init(testing.allocator); | |
| 2732 | defer q.deinit(); | |
| 2733 | var r = try Int.init(testing.allocator); | |
| 2734 | defer r.deinit(); | |
| 2735 | try Int.divTrunc(&q, &r, a, b); | |
| 1874 | while (i < a.len) : (i += 1) { | |
| 1875 | var c: Limb = 0; | |
| 1876 | c += @boolToInt(@addWithOverflow(Limb, r[i], a[i], &r[i])); | |
| 1877 | c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); | |
| 1878 | carry = c; | |
| 1879 | } | |
| 2736 | 1880 | |
| 2737 | const qs = try q.toString(testing.allocator, 16, false); | |
| 2738 | defer testing.allocator.free(qs); | |
| 2739 | testing.expect(std.mem.eql(u8, qs, "40100400fe3f8fe3f8fe3f8fe3f8fe3f8fe4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f91e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4992649926499264991e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4792e4b92e4b92e4b92e4b92a4a92a4a92a4")); | |
| 1881 | while ((carry != 0) and i < r.len) : (i += 1) { | |
| 1882 | carry = @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); | |
| 1883 | } | |
| 2740 | 1884 | |
| 2741 | const rs = try r.toString(testing.allocator, 16, false); | |
| 2742 | defer testing.allocator.free(rs); | |
| 2743 | testing.expect(std.mem.eql(u8, rs, "a900000000000000000000000000000000000000000000000000")); | |
| 1885 | return carry; | |
| 2744 | 1886 | } |
| 2745 | 1887 | |
| 2746 | test "big.int shift-right single" { | |
| 2747 | var a = try Int.initSet(testing.allocator, 0xffff0000); | |
| 2748 | defer a.deinit(); | |
| 2749 | try a.shiftRight(a, 16); | |
| 2750 | ||
| 2751 | testing.expect((try a.to(u32)) == 0xffff); | |
| 2752 | } | |
| 1888 | /// Returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively for limbs. | |
| 1889 | pub fn llcmp(a: []const Limb, b: []const Limb) i8 { | |
| 1890 | @setRuntimeSafety(false); | |
| 1891 | const a_len = llnormalize(a); | |
| 1892 | const b_len = llnormalize(b); | |
| 1893 | if (a_len < b_len) { | |
| 1894 | return -1; | |
| 1895 | } | |
| 1896 | if (a_len > b_len) { | |
| 1897 | return 1; | |
| 1898 | } | |
| 2753 | 1899 | |
| 2754 | test "big.int shift-right multi" { | |
| 2755 | var a = try Int.initSet(testing.allocator, 0xffff0000eeee1111dddd2222cccc3333); | |
| 2756 | defer a.deinit(); | |
| 2757 | try a.shiftRight(a, 67); | |
| 1900 | var i: usize = a_len - 1; | |
| 1901 | while (i != 0) : (i -= 1) { | |
| 1902 | if (a[i] != b[i]) { | |
| 1903 | break; | |
| 1904 | } | |
| 1905 | } | |
| 2758 | 1906 | |
| 2759 | testing.expect((try a.to(u64)) == 0x1fffe0001dddc222); | |
| 1907 | if (a[i] < b[i]) { | |
| 1908 | return -1; | |
| 1909 | } else if (a[i] > b[i]) { | |
| 1910 | return 1; | |
| 1911 | } else { | |
| 1912 | return 0; | |
| 1913 | } | |
| 2760 | 1914 | } |
| 2761 | 1915 | |
| 2762 | test "big.int shift-left single" { | |
| 2763 | var a = try Int.initSet(testing.allocator, 0xffff); | |
| 2764 | defer a.deinit(); | |
| 2765 | try a.shiftLeft(a, 16); | |
| 1916 | fn llmulDigit(acc: []Limb, y: []const Limb, xi: Limb) void { | |
| 1917 | @setRuntimeSafety(false); | |
| 1918 | if (xi == 0) { | |
| 1919 | return; | |
| 1920 | } | |
| 2766 | 1921 | |
| 2767 | testing.expect((try a.to(u64)) == 0xffff0000); | |
| 2768 | } | |
| 1922 | var carry: usize = 0; | |
| 1923 | var a_lo = acc[0..y.len]; | |
| 1924 | var a_hi = acc[y.len..]; | |
| 2769 | 1925 | |
| 2770 | test "big.int shift-left multi" { | |
| 2771 | var a = try Int.initSet(testing.allocator, 0x1fffe0001dddc222); | |
| 2772 | defer a.deinit(); | |
| 2773 | try a.shiftLeft(a, 67); | |
| 1926 | var j: usize = 0; | |
| 1927 | while (j < a_lo.len) : (j += 1) { | |
| 1928 | a_lo[j] = @call(.{ .modifier = .always_inline }, addMulLimbWithCarry, .{ a_lo[j], y[j], xi, &carry }); | |
| 1929 | } | |
| 2774 | 1930 | |
| 2775 | testing.expect((try a.to(u128)) == 0xffff0000eeee11100000000000000000); | |
| 1931 | j = 0; | |
| 1932 | while ((carry != 0) and (j < a_hi.len)) : (j += 1) { | |
| 1933 | carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j])); | |
| 1934 | } | |
| 2776 | 1935 | } |
| 2777 | 1936 | |
| 2778 | test "big.int shift-right negative" { | |
| 2779 | var a = try Int.init(testing.allocator); | |
| 2780 | defer a.deinit(); | |
| 2781 | ||
| 2782 | try a.shiftRight(try Int.initSet(testing.allocator, -20), 2); | |
| 2783 | defer a.deinit(); | |
| 2784 | testing.expect((try a.to(i32)) == -20 >> 2); | |
| 1937 | /// returns the min length the limb could be. | |
| 1938 | fn llnormalize(a: []const Limb) usize { | |
| 1939 | @setRuntimeSafety(false); | |
| 1940 | var j = a.len; | |
| 1941 | while (j > 0) : (j -= 1) { | |
| 1942 | if (a[j - 1] != 0) { | |
| 1943 | break; | |
| 1944 | } | |
| 1945 | } | |
| 2785 | 1946 | |
| 2786 | try a.shiftRight(try Int.initSet(testing.allocator, -5), 10); | |
| 2787 | defer a.deinit(); | |
| 2788 | testing.expect((try a.to(i32)) == -5 >> 10); | |
| 1947 | // Handle zero | |
| 1948 | return if (j != 0) j else 1; | |
| 2789 | 1949 | } |
| 2790 | 1950 | |
| 2791 | test "big.int shift-left negative" { | |
| 2792 | var a = try Int.init(testing.allocator); | |
| 2793 | defer a.deinit(); | |
| 1951 | /// Knuth 4.3.1, Algorithm S. | |
| 1952 | fn llsub(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 1953 | @setRuntimeSafety(false); | |
| 1954 | assert(a.len != 0 and b.len != 0); | |
| 1955 | assert(a.len > b.len or (a.len == b.len and a[a.len - 1] >= b[b.len - 1])); | |
| 1956 | assert(r.len >= a.len); | |
| 2794 | 1957 | |
| 2795 | try a.shiftRight(try Int.initSet(testing.allocator, -10), 1232); | |
| 2796 | defer a.deinit(); | |
| 2797 | testing.expect((try a.to(i32)) == -10 >> 1232); | |
| 2798 | } | |
| 1958 | var i: usize = 0; | |
| 1959 | var borrow: Limb = 0; | |
| 2799 | 1960 | |
| 2800 | test "big.int bitwise and simple" { | |
| 2801 | var a = try Int.initSet(testing.allocator, 0xffffffff11111111); | |
| 2802 | defer a.deinit(); | |
| 2803 | var b = try Int.initSet(testing.allocator, 0xeeeeeeee22222222); | |
| 2804 | defer b.deinit(); | |
| 1961 | while (i < b.len) : (i += 1) { | |
| 1962 | var c: Limb = 0; | |
| 1963 | c += @boolToInt(@subWithOverflow(Limb, a[i], b[i], &r[i])); | |
| 1964 | c += @boolToInt(@subWithOverflow(Limb, r[i], borrow, &r[i])); | |
| 1965 | borrow = c; | |
| 1966 | } | |
| 2805 | 1967 | |
| 2806 | try a.bitAnd(a, b); | |
| 1968 | while (i < a.len) : (i += 1) { | |
| 1969 | borrow = @boolToInt(@subWithOverflow(Limb, a[i], borrow, &r[i])); | |
| 1970 | } | |
| 2807 | 1971 | |
| 2808 | testing.expect((try a.to(u64)) == 0xeeeeeeee00000000); | |
| 1972 | assert(borrow == 0); | |
| 2809 | 1973 | } |
| 2810 | 1974 | |
| 2811 | test "big.int bitwise and multi-limb" { | |
| 2812 | var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 2813 | defer a.deinit(); | |
| 2814 | var b = try Int.initSet(testing.allocator, maxInt(Limb)); | |
| 2815 | defer b.deinit(); | |
| 2816 | ||
| 2817 | try a.bitAnd(a, b); | |
| 1975 | /// Knuth 4.3.1, Algorithm A. | |
| 1976 | fn lladd(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 1977 | @setRuntimeSafety(false); | |
| 1978 | assert(a.len != 0 and b.len != 0); | |
| 1979 | assert(a.len >= b.len); | |
| 1980 | assert(r.len >= a.len + 1); | |
| 2818 | 1981 | |
| 2819 | testing.expect((try a.to(u128)) == 0); | |
| 2820 | } | |
| 1982 | var i: usize = 0; | |
| 1983 | var carry: Limb = 0; | |
| 2821 | 1984 | |
| 2822 | test "big.int bitwise xor simple" { | |
| 2823 | var a = try Int.initSet(testing.allocator, 0xffffffff11111111); | |
| 2824 | defer a.deinit(); | |
| 2825 | var b = try Int.initSet(testing.allocator, 0xeeeeeeee22222222); | |
| 2826 | defer b.deinit(); | |
| 1985 | while (i < b.len) : (i += 1) { | |
| 1986 | var c: Limb = 0; | |
| 1987 | c += @boolToInt(@addWithOverflow(Limb, a[i], b[i], &r[i])); | |
| 1988 | c += @boolToInt(@addWithOverflow(Limb, r[i], carry, &r[i])); | |
| 1989 | carry = c; | |
| 1990 | } | |
| 2827 | 1991 | |
| 2828 | try a.bitXor(a, b); | |
| 1992 | while (i < a.len) : (i += 1) { | |
| 1993 | carry = @boolToInt(@addWithOverflow(Limb, a[i], carry, &r[i])); | |
| 1994 | } | |
| 2829 | 1995 | |
| 2830 | testing.expect((try a.to(u64)) == 0x1111111133333333); | |
| 1996 | r[i] = carry; | |
| 2831 | 1997 | } |
| 2832 | 1998 | |
| 2833 | test "big.int bitwise xor multi-limb" { | |
| 2834 | var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 2835 | defer a.deinit(); | |
| 2836 | var b = try Int.initSet(testing.allocator, maxInt(Limb)); | |
| 2837 | defer b.deinit(); | |
| 1999 | /// Knuth 4.3.1, Exercise 16. | |
| 2000 | fn lldiv1(quo: []Limb, rem: *Limb, a: []const Limb, b: Limb) void { | |
| 2001 | @setRuntimeSafety(false); | |
| 2002 | assert(a.len > 1 or a[0] >= b); | |
| 2003 | assert(quo.len >= a.len); | |
| 2838 | 2004 | |
| 2839 | try a.bitXor(a, b); | |
| 2005 | rem.* = 0; | |
| 2006 | for (a) |_, ri| { | |
| 2007 | const i = a.len - ri - 1; | |
| 2008 | const pdiv = ((@as(DoubleLimb, rem.*) << Limb.bit_count) | a[i]); | |
| 2840 | 2009 | |
| 2841 | testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb)); | |
| 2010 | if (pdiv == 0) { | |
| 2011 | quo[i] = 0; | |
| 2012 | rem.* = 0; | |
| 2013 | } else if (pdiv < b) { | |
| 2014 | quo[i] = 0; | |
| 2015 | rem.* = @truncate(Limb, pdiv); | |
| 2016 | } else if (pdiv == b) { | |
| 2017 | quo[i] = 1; | |
| 2018 | rem.* = 0; | |
| 2019 | } else { | |
| 2020 | quo[i] = @truncate(Limb, @divTrunc(pdiv, b)); | |
| 2021 | rem.* = @truncate(Limb, pdiv - (quo[i] *% b)); | |
| 2022 | } | |
| 2023 | } | |
| 2842 | 2024 | } |
| 2843 | 2025 | |
| 2844 | test "big.int bitwise or simple" { | |
| 2845 | var a = try Int.initSet(testing.allocator, 0xffffffff11111111); | |
| 2846 | defer a.deinit(); | |
| 2847 | var b = try Int.initSet(testing.allocator, 0xeeeeeeee22222222); | |
| 2848 | defer b.deinit(); | |
| 2026 | fn llshl(r: []Limb, a: []const Limb, shift: usize) void { | |
| 2027 | @setRuntimeSafety(false); | |
| 2028 | assert(a.len >= 1); | |
| 2029 | assert(r.len >= a.len + (shift / Limb.bit_count) + 1); | |
| 2849 | 2030 | |
| 2850 | try a.bitOr(a, b); | |
| 2031 | const limb_shift = shift / Limb.bit_count + 1; | |
| 2032 | const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count); | |
| 2851 | 2033 | |
| 2852 | testing.expect((try a.to(u64)) == 0xffffffff33333333); | |
| 2853 | } | |
| 2854 | ||
| 2855 | test "big.int bitwise or multi-limb" { | |
| 2856 | var a = try Int.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 2857 | defer a.deinit(); | |
| 2858 | var b = try Int.initSet(testing.allocator, maxInt(Limb)); | |
| 2859 | defer b.deinit(); | |
| 2034 | var carry: Limb = 0; | |
| 2035 | var i: usize = 0; | |
| 2036 | while (i < a.len) : (i += 1) { | |
| 2037 | const src_i = a.len - i - 1; | |
| 2038 | const dst_i = src_i + limb_shift; | |
| 2860 | 2039 | |
| 2861 | try a.bitOr(a, b); | |
| 2040 | const src_digit = a[src_i]; | |
| 2041 | r[dst_i] = carry | @call(.{ .modifier = .always_inline }, math.shr, .{ | |
| 2042 | Limb, | |
| 2043 | src_digit, | |
| 2044 | Limb.bit_count - @intCast(Limb, interior_limb_shift), | |
| 2045 | }); | |
| 2046 | carry = (src_digit << interior_limb_shift); | |
| 2047 | } | |
| 2862 | 2048 | |
| 2863 | // TODO: big.int.cpp or is wrong on multi-limb. | |
| 2864 | testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb)); | |
| 2049 | r[limb_shift - 1] = carry; | |
| 2050 | mem.set(Limb, r[0 .. limb_shift - 1], 0); | |
| 2865 | 2051 | } |
| 2866 | 2052 | |
| 2867 | test "big.int var args" { | |
| 2868 | var a = try Int.initSet(testing.allocator, 5); | |
| 2869 | defer a.deinit(); | |
| 2053 | fn llshr(r: []Limb, a: []const Limb, shift: usize) void { | |
| 2054 | @setRuntimeSafety(false); | |
| 2055 | assert(a.len >= 1); | |
| 2056 | assert(r.len >= a.len - (shift / Limb.bit_count)); | |
| 2870 | 2057 | |
| 2871 | const b = try Int.initSet(testing.allocator, 6); | |
| 2872 | defer b.deinit(); | |
| 2873 | try a.add(a, b); | |
| 2874 | testing.expect((try a.to(u64)) == 11); | |
| 2058 | const limb_shift = shift / Limb.bit_count; | |
| 2059 | const interior_limb_shift = @intCast(Log2Limb, shift % Limb.bit_count); | |
| 2875 | 2060 | |
| 2876 | const c = try Int.initSet(testing.allocator, 11); | |
| 2877 | defer c.deinit(); | |
| 2878 | testing.expect(a.cmp(c) == .eq); | |
| 2061 | var carry: Limb = 0; | |
| 2062 | var i: usize = 0; | |
| 2063 | while (i < a.len - limb_shift) : (i += 1) { | |
| 2064 | const src_i = a.len - i - 1; | |
| 2065 | const dst_i = src_i - limb_shift; | |
| 2879 | 2066 | |
| 2880 | const d = try Int.initSet(testing.allocator, 14); | |
| 2881 | defer d.deinit(); | |
| 2882 | testing.expect(a.cmp(d) != .gt); | |
| 2067 | const src_digit = a[src_i]; | |
| 2068 | r[dst_i] = carry | (src_digit >> interior_limb_shift); | |
| 2069 | carry = @call(.{ .modifier = .always_inline }, math.shl, .{ | |
| 2070 | Limb, | |
| 2071 | src_digit, | |
| 2072 | Limb.bit_count - @intCast(Limb, interior_limb_shift), | |
| 2073 | }); | |
| 2074 | } | |
| 2883 | 2075 | } |
| 2884 | 2076 | |
| 2885 | test "big.int gcd non-one small" { | |
| 2886 | var a = try Int.initSet(testing.allocator, 17); | |
| 2887 | defer a.deinit(); | |
| 2888 | var b = try Int.initSet(testing.allocator, 97); | |
| 2889 | defer b.deinit(); | |
| 2890 | var r = try Int.init(testing.allocator); | |
| 2891 | defer r.deinit(); | |
| 2077 | fn llor(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 2078 | @setRuntimeSafety(false); | |
| 2079 | assert(r.len >= a.len); | |
| 2080 | assert(a.len >= b.len); | |
| 2892 | 2081 | |
| 2893 | try r.gcd(a, b); | |
| 2894 | ||
| 2895 | testing.expect((try r.to(u32)) == 1); | |
| 2082 | var i: usize = 0; | |
| 2083 | while (i < b.len) : (i += 1) { | |
| 2084 | r[i] = a[i] | b[i]; | |
| 2085 | } | |
| 2086 | while (i < a.len) : (i += 1) { | |
| 2087 | r[i] = a[i]; | |
| 2088 | } | |
| 2896 | 2089 | } |
| 2897 | 2090 | |
| 2898 | test "big.int gcd non-one small" { | |
| 2899 | var a = try Int.initSet(testing.allocator, 4864); | |
| 2900 | defer a.deinit(); | |
| 2901 | var b = try Int.initSet(testing.allocator, 3458); | |
| 2902 | defer b.deinit(); | |
| 2903 | var r = try Int.init(testing.allocator); | |
| 2904 | defer r.deinit(); | |
| 2905 | ||
| 2906 | try r.gcd(a, b); | |
| 2091 | fn lland(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 2092 | @setRuntimeSafety(false); | |
| 2093 | assert(r.len >= b.len); | |
| 2094 | assert(a.len >= b.len); | |
| 2907 | 2095 | |
| 2908 | testing.expect((try r.to(u32)) == 38); | |
| 2096 | var i: usize = 0; | |
| 2097 | while (i < b.len) : (i += 1) { | |
| 2098 | r[i] = a[i] & b[i]; | |
| 2099 | } | |
| 2909 | 2100 | } |
| 2910 | 2101 | |
| 2911 | test "big.int gcd non-one large" { | |
| 2912 | var a = try Int.initSet(testing.allocator, 0xffffffffffffffff); | |
| 2913 | defer a.deinit(); | |
| 2914 | var b = try Int.initSet(testing.allocator, 0xffffffffffffffff7777); | |
| 2915 | defer b.deinit(); | |
| 2916 | var r = try Int.init(testing.allocator); | |
| 2917 | defer r.deinit(); | |
| 2918 | ||
| 2919 | try r.gcd(a, b); | |
| 2102 | fn llxor(r: []Limb, a: []const Limb, b: []const Limb) void { | |
| 2103 | assert(r.len >= a.len); | |
| 2104 | assert(a.len >= b.len); | |
| 2920 | 2105 | |
| 2921 | testing.expect((try r.to(u32)) == 4369); | |
| 2106 | var i: usize = 0; | |
| 2107 | while (i < b.len) : (i += 1) { | |
| 2108 | r[i] = a[i] ^ b[i]; | |
| 2109 | } | |
| 2110 | while (i < a.len) : (i += 1) { | |
| 2111 | r[i] = a[i]; | |
| 2112 | } | |
| 2922 | 2113 | } |
| 2923 | 2114 | |
| 2924 | test "big.int gcd large multi-limb result" { | |
| 2925 | var a = try Int.initSet(testing.allocator, 0x12345678123456781234567812345678123456781234567812345678); | |
| 2926 | defer a.deinit(); | |
| 2927 | var b = try Int.initSet(testing.allocator, 0x12345671234567123456712345671234567123456712345671234567); | |
| 2928 | defer b.deinit(); | |
| 2929 | var r = try Int.init(testing.allocator); | |
| 2930 | defer r.deinit(); | |
| 2931 | ||
| 2932 | try r.gcd(a, b); | |
| 2115 | // Storage must live for the lifetime of the returned value | |
| 2116 | fn fixedIntFromSignedDoubleLimb(A: SignedDoubleLimb, storage: []Limb) Mutable { | |
| 2117 | assert(storage.len >= 2); | |
| 2933 | 2118 | |
| 2934 | testing.expect((try r.to(u256)) == 0xf000000ff00000fff0000ffff000fffff00ffffff1); | |
| 2119 | const A_is_positive = A >= 0; | |
| 2120 | const Au = @intCast(DoubleLimb, if (A < 0) -A else A); | |
| 2121 | storage[0] = @truncate(Limb, Au); | |
| 2122 | storage[1] = @truncate(Limb, Au >> Limb.bit_count); | |
| 2123 | return .{ | |
| 2124 | .limbs = storage[0..2], | |
| 2125 | .positive = A_is_positive, | |
| 2126 | .len = 2, | |
| 2127 | }; | |
| 2935 | 2128 | } |
| 2936 | 2129 | |
| 2937 | test "big.int gcd one large" { | |
| 2938 | var a = try Int.initSet(testing.allocator, 1897056385327307); | |
| 2939 | defer a.deinit(); | |
| 2940 | var b = try Int.initSet(testing.allocator, 2251799813685248); | |
| 2941 | defer b.deinit(); | |
| 2942 | var r = try Int.init(testing.allocator); | |
| 2943 | defer r.deinit(); | |
| 2944 | ||
| 2945 | try r.gcd(a, b); | |
| 2946 | ||
| 2947 | testing.expect((try r.to(u64)) == 1); | |
| 2130 | test "" { | |
| 2131 | _ = @import("int_test.zig"); | |
| 2948 | 2132 | } |
lib/std/math/big/int_test.zig created+1455| ... | ... | @@ -0,0 +1,1455 @@ |
| 1 | const std = @import("../../std.zig"); | |
| 2 | const mem = std.mem; | |
| 3 | const testing = std.testing; | |
| 4 | const Managed = std.math.big.int.Managed; | |
| 5 | const Limb = std.math.big.Limb; | |
| 6 | const DoubleLimb = std.math.big.DoubleLimb; | |
| 7 | const maxInt = std.math.maxInt; | |
| 8 | const minInt = std.math.minInt; | |
| 9 | ||
| 10 | // NOTE: All the following tests assume the max machine-word will be 64-bit. | |
| 11 | // | |
| 12 | // They will still run on larger than this and should pass, but the multi-limb code-paths | |
| 13 | // may be untested in some cases. | |
| 14 | ||
| 15 | test "big.int comptime_int set" { | |
| 16 | comptime var s = 0xefffffff00000001eeeeeeefaaaaaaab; | |
| 17 | var a = try Managed.initSet(testing.allocator, s); | |
| 18 | defer a.deinit(); | |
| 19 | ||
| 20 | const s_limb_count = 128 / Limb.bit_count; | |
| 21 | ||
| 22 | comptime var i: usize = 0; | |
| 23 | inline while (i < s_limb_count) : (i += 1) { | |
| 24 | const result = @as(Limb, s & maxInt(Limb)); | |
| 25 | s >>= Limb.bit_count / 2; | |
| 26 | s >>= Limb.bit_count / 2; | |
| 27 | testing.expect(a.limbs[i] == result); | |
| 28 | } | |
| 29 | } | |
| 30 | ||
| 31 | test "big.int comptime_int set negative" { | |
| 32 | var a = try Managed.initSet(testing.allocator, -10); | |
| 33 | defer a.deinit(); | |
| 34 | ||
| 35 | testing.expect(a.limbs[0] == 10); | |
| 36 | testing.expect(a.isPositive() == false); | |
| 37 | } | |
| 38 | ||
| 39 | test "big.int int set unaligned small" { | |
| 40 | var a = try Managed.initSet(testing.allocator, @as(u7, 45)); | |
| 41 | defer a.deinit(); | |
| 42 | ||
| 43 | testing.expect(a.limbs[0] == 45); | |
| 44 | testing.expect(a.isPositive() == true); | |
| 45 | } | |
| 46 | ||
| 47 | test "big.int comptime_int to" { | |
| 48 | var a = try Managed.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab); | |
| 49 | defer a.deinit(); | |
| 50 | ||
| 51 | testing.expect((try a.to(u128)) == 0xefffffff00000001eeeeeeefaaaaaaab); | |
| 52 | } | |
| 53 | ||
| 54 | test "big.int sub-limb to" { | |
| 55 | var a = try Managed.initSet(testing.allocator, 10); | |
| 56 | defer a.deinit(); | |
| 57 | ||
| 58 | testing.expect((try a.to(u8)) == 10); | |
| 59 | } | |
| 60 | ||
| 61 | test "big.int to target too small error" { | |
| 62 | var a = try Managed.initSet(testing.allocator, 0xffffffff); | |
| 63 | defer a.deinit(); | |
| 64 | ||
| 65 | testing.expectError(error.TargetTooSmall, a.to(u8)); | |
| 66 | } | |
| 67 | ||
| 68 | test "big.int normalize" { | |
| 69 | var a = try Managed.init(testing.allocator); | |
| 70 | defer a.deinit(); | |
| 71 | try a.ensureCapacity(8); | |
| 72 | ||
| 73 | a.limbs[0] = 1; | |
| 74 | a.limbs[1] = 2; | |
| 75 | a.limbs[2] = 3; | |
| 76 | a.limbs[3] = 0; | |
| 77 | a.normalize(4); | |
| 78 | testing.expect(a.len() == 3); | |
| 79 | ||
| 80 | a.limbs[0] = 1; | |
| 81 | a.limbs[1] = 2; | |
| 82 | a.limbs[2] = 3; | |
| 83 | a.normalize(3); | |
| 84 | testing.expect(a.len() == 3); | |
| 85 | ||
| 86 | a.limbs[0] = 0; | |
| 87 | a.limbs[1] = 0; | |
| 88 | a.normalize(2); | |
| 89 | testing.expect(a.len() == 1); | |
| 90 | ||
| 91 | a.limbs[0] = 0; | |
| 92 | a.normalize(1); | |
| 93 | testing.expect(a.len() == 1); | |
| 94 | } | |
| 95 | ||
| 96 | test "big.int normalize multi" { | |
| 97 | var a = try Managed.init(testing.allocator); | |
| 98 | defer a.deinit(); | |
| 99 | try a.ensureCapacity(8); | |
| 100 | ||
| 101 | a.limbs[0] = 1; | |
| 102 | a.limbs[1] = 2; | |
| 103 | a.limbs[2] = 0; | |
| 104 | a.limbs[3] = 0; | |
| 105 | a.normalize(4); | |
| 106 | testing.expect(a.len() == 2); | |
| 107 | ||
| 108 | a.limbs[0] = 1; | |
| 109 | a.limbs[1] = 2; | |
| 110 | a.limbs[2] = 3; | |
| 111 | a.normalize(3); | |
| 112 | testing.expect(a.len() == 3); | |
| 113 | ||
| 114 | a.limbs[0] = 0; | |
| 115 | a.limbs[1] = 0; | |
| 116 | a.limbs[2] = 0; | |
| 117 | a.limbs[3] = 0; | |
| 118 | a.normalize(4); | |
| 119 | testing.expect(a.len() == 1); | |
| 120 | ||
| 121 | a.limbs[0] = 0; | |
| 122 | a.normalize(1); | |
| 123 | testing.expect(a.len() == 1); | |
| 124 | } | |
| 125 | ||
| 126 | test "big.int parity" { | |
| 127 | var a = try Managed.init(testing.allocator); | |
| 128 | defer a.deinit(); | |
| 129 | ||
| 130 | try a.set(0); | |
| 131 | testing.expect(a.isEven()); | |
| 132 | testing.expect(!a.isOdd()); | |
| 133 | ||
| 134 | try a.set(7); | |
| 135 | testing.expect(!a.isEven()); | |
| 136 | testing.expect(a.isOdd()); | |
| 137 | } | |
| 138 | ||
| 139 | test "big.int bitcount + sizeInBaseUpperBound" { | |
| 140 | var a = try Managed.init(testing.allocator); | |
| 141 | defer a.deinit(); | |
| 142 | ||
| 143 | try a.set(0b100); | |
| 144 | testing.expect(a.bitCountAbs() == 3); | |
| 145 | testing.expect(a.sizeInBaseUpperBound(2) >= 3); | |
| 146 | testing.expect(a.sizeInBaseUpperBound(10) >= 1); | |
| 147 | ||
| 148 | a.negate(); | |
| 149 | testing.expect(a.bitCountAbs() == 3); | |
| 150 | testing.expect(a.sizeInBaseUpperBound(2) >= 4); | |
| 151 | testing.expect(a.sizeInBaseUpperBound(10) >= 2); | |
| 152 | ||
| 153 | try a.set(0xffffffff); | |
| 154 | testing.expect(a.bitCountAbs() == 32); | |
| 155 | testing.expect(a.sizeInBaseUpperBound(2) >= 32); | |
| 156 | testing.expect(a.sizeInBaseUpperBound(10) >= 10); | |
| 157 | ||
| 158 | try a.shiftLeft(a, 5000); | |
| 159 | testing.expect(a.bitCountAbs() == 5032); | |
| 160 | testing.expect(a.sizeInBaseUpperBound(2) >= 5032); | |
| 161 | a.setSign(false); | |
| 162 | ||
| 163 | testing.expect(a.bitCountAbs() == 5032); | |
| 164 | testing.expect(a.sizeInBaseUpperBound(2) >= 5033); | |
| 165 | } | |
| 166 | ||
| 167 | test "big.int bitcount/to" { | |
| 168 | var a = try Managed.init(testing.allocator); | |
| 169 | defer a.deinit(); | |
| 170 | ||
| 171 | try a.set(0); | |
| 172 | testing.expect(a.bitCountTwosComp() == 0); | |
| 173 | ||
| 174 | testing.expect((try a.to(u0)) == 0); | |
| 175 | testing.expect((try a.to(i0)) == 0); | |
| 176 | ||
| 177 | try a.set(-1); | |
| 178 | testing.expect(a.bitCountTwosComp() == 1); | |
| 179 | testing.expect((try a.to(i1)) == -1); | |
| 180 | ||
| 181 | try a.set(-8); | |
| 182 | testing.expect(a.bitCountTwosComp() == 4); | |
| 183 | testing.expect((try a.to(i4)) == -8); | |
| 184 | ||
| 185 | try a.set(127); | |
| 186 | testing.expect(a.bitCountTwosComp() == 7); | |
| 187 | testing.expect((try a.to(u7)) == 127); | |
| 188 | ||
| 189 | try a.set(-128); | |
| 190 | testing.expect(a.bitCountTwosComp() == 8); | |
| 191 | testing.expect((try a.to(i8)) == -128); | |
| 192 | ||
| 193 | try a.set(-129); | |
| 194 | testing.expect(a.bitCountTwosComp() == 9); | |
| 195 | testing.expect((try a.to(i9)) == -129); | |
| 196 | } | |
| 197 | ||
| 198 | test "big.int fits" { | |
| 199 | var a = try Managed.init(testing.allocator); | |
| 200 | defer a.deinit(); | |
| 201 | ||
| 202 | try a.set(0); | |
| 203 | testing.expect(a.fits(u0)); | |
| 204 | testing.expect(a.fits(i0)); | |
| 205 | ||
| 206 | try a.set(255); | |
| 207 | testing.expect(!a.fits(u0)); | |
| 208 | testing.expect(!a.fits(u1)); | |
| 209 | testing.expect(!a.fits(i8)); | |
| 210 | testing.expect(a.fits(u8)); | |
| 211 | testing.expect(a.fits(u9)); | |
| 212 | testing.expect(a.fits(i9)); | |
| 213 | ||
| 214 | try a.set(-128); | |
| 215 | testing.expect(!a.fits(i7)); | |
| 216 | testing.expect(a.fits(i8)); | |
| 217 | testing.expect(a.fits(i9)); | |
| 218 | testing.expect(!a.fits(u9)); | |
| 219 | ||
| 220 | try a.set(0x1ffffffffeeeeeeee); | |
| 221 | testing.expect(!a.fits(u32)); | |
| 222 | testing.expect(!a.fits(u64)); | |
| 223 | testing.expect(a.fits(u65)); | |
| 224 | } | |
| 225 | ||
| 226 | test "big.int string set" { | |
| 227 | var a = try Managed.init(testing.allocator); | |
| 228 | defer a.deinit(); | |
| 229 | ||
| 230 | try a.setString(10, "120317241209124781241290847124"); | |
| 231 | testing.expect((try a.to(u128)) == 120317241209124781241290847124); | |
| 232 | } | |
| 233 | ||
| 234 | test "big.int string negative" { | |
| 235 | var a = try Managed.init(testing.allocator); | |
| 236 | defer a.deinit(); | |
| 237 | ||
| 238 | try a.setString(10, "-1023"); | |
| 239 | testing.expect((try a.to(i32)) == -1023); | |
| 240 | } | |
| 241 | ||
| 242 | test "big.int string set number with underscores" { | |
| 243 | var a = try Managed.init(testing.allocator); | |
| 244 | defer a.deinit(); | |
| 245 | ||
| 246 | try a.setString(10, "__1_2_0_3_1_7_2_4_1_2_0_____9_1__2__4_7_8_1_2_4_1_2_9_0_8_4_7_1_2_4___"); | |
| 247 | testing.expect((try a.to(u128)) == 120317241209124781241290847124); | |
| 248 | } | |
| 249 | ||
| 250 | test "big.int string set case insensitive number" { | |
| 251 | var a = try Managed.init(testing.allocator); | |
| 252 | defer a.deinit(); | |
| 253 | ||
| 254 | try a.setString(16, "aB_cD_eF"); | |
| 255 | testing.expect((try a.to(u32)) == 0xabcdef); | |
| 256 | } | |
| 257 | ||
| 258 | test "big.int string set bad char error" { | |
| 259 | var a = try Managed.init(testing.allocator); | |
| 260 | defer a.deinit(); | |
| 261 | testing.expectError(error.InvalidCharacter, a.setString(10, "x")); | |
| 262 | } | |
| 263 | ||
| 264 | test "big.int string set bad base error" { | |
| 265 | var a = try Managed.init(testing.allocator); | |
| 266 | defer a.deinit(); | |
| 267 | testing.expectError(error.InvalidBase, a.setString(45, "10")); | |
| 268 | } | |
| 269 | ||
| 270 | test "big.int string to" { | |
| 271 | var a = try Managed.initSet(testing.allocator, 120317241209124781241290847124); | |
| 272 | defer a.deinit(); | |
| 273 | ||
| 274 | const as = try a.toString(testing.allocator, 10, false); | |
| 275 | defer testing.allocator.free(as); | |
| 276 | const es = "120317241209124781241290847124"; | |
| 277 | ||
| 278 | testing.expect(mem.eql(u8, as, es)); | |
| 279 | } | |
| 280 | ||
| 281 | test "big.int string to base base error" { | |
| 282 | var a = try Managed.initSet(testing.allocator, 0xffffffff); | |
| 283 | defer a.deinit(); | |
| 284 | ||
| 285 | testing.expectError(error.InvalidBase, a.toString(testing.allocator, 45, false)); | |
| 286 | } | |
| 287 | ||
| 288 | test "big.int string to base 2" { | |
| 289 | var a = try Managed.initSet(testing.allocator, -0b1011); | |
| 290 | defer a.deinit(); | |
| 291 | ||
| 292 | const as = try a.toString(testing.allocator, 2, false); | |
| 293 | defer testing.allocator.free(as); | |
| 294 | const es = "-1011"; | |
| 295 | ||
| 296 | testing.expect(mem.eql(u8, as, es)); | |
| 297 | } | |
| 298 | ||
| 299 | test "big.int string to base 16" { | |
| 300 | var a = try Managed.initSet(testing.allocator, 0xefffffff00000001eeeeeeefaaaaaaab); | |
| 301 | defer a.deinit(); | |
| 302 | ||
| 303 | const as = try a.toString(testing.allocator, 16, false); | |
| 304 | defer testing.allocator.free(as); | |
| 305 | const es = "efffffff00000001eeeeeeefaaaaaaab"; | |
| 306 | ||
| 307 | testing.expect(mem.eql(u8, as, es)); | |
| 308 | } | |
| 309 | ||
| 310 | test "big.int neg string to" { | |
| 311 | var a = try Managed.initSet(testing.allocator, -123907434); | |
| 312 | defer a.deinit(); | |
| 313 | ||
| 314 | const as = try a.toString(testing.allocator, 10, false); | |
| 315 | defer testing.allocator.free(as); | |
| 316 | const es = "-123907434"; | |
| 317 | ||
| 318 | testing.expect(mem.eql(u8, as, es)); | |
| 319 | } | |
| 320 | ||
| 321 | test "big.int zero string to" { | |
| 322 | var a = try Managed.initSet(testing.allocator, 0); | |
| 323 | defer a.deinit(); | |
| 324 | ||
| 325 | const as = try a.toString(testing.allocator, 10, false); | |
| 326 | defer testing.allocator.free(as); | |
| 327 | const es = "0"; | |
| 328 | ||
| 329 | testing.expect(mem.eql(u8, as, es)); | |
| 330 | } | |
| 331 | ||
| 332 | test "big.int clone" { | |
| 333 | var a = try Managed.initSet(testing.allocator, 1234); | |
| 334 | defer a.deinit(); | |
| 335 | var b = try a.clone(); | |
| 336 | defer b.deinit(); | |
| 337 | ||
| 338 | testing.expect((try a.to(u32)) == 1234); | |
| 339 | testing.expect((try b.to(u32)) == 1234); | |
| 340 | ||
| 341 | try a.set(77); | |
| 342 | testing.expect((try a.to(u32)) == 77); | |
| 343 | testing.expect((try b.to(u32)) == 1234); | |
| 344 | } | |
| 345 | ||
| 346 | test "big.int swap" { | |
| 347 | var a = try Managed.initSet(testing.allocator, 1234); | |
| 348 | defer a.deinit(); | |
| 349 | var b = try Managed.initSet(testing.allocator, 5678); | |
| 350 | defer b.deinit(); | |
| 351 | ||
| 352 | testing.expect((try a.to(u32)) == 1234); | |
| 353 | testing.expect((try b.to(u32)) == 5678); | |
| 354 | ||
| 355 | a.swap(&b); | |
| 356 | ||
| 357 | testing.expect((try a.to(u32)) == 5678); | |
| 358 | testing.expect((try b.to(u32)) == 1234); | |
| 359 | } | |
| 360 | ||
| 361 | test "big.int to negative" { | |
| 362 | var a = try Managed.initSet(testing.allocator, -10); | |
| 363 | defer a.deinit(); | |
| 364 | ||
| 365 | testing.expect((try a.to(i32)) == -10); | |
| 366 | } | |
| 367 | ||
| 368 | test "big.int compare" { | |
| 369 | var a = try Managed.initSet(testing.allocator, -11); | |
| 370 | defer a.deinit(); | |
| 371 | var b = try Managed.initSet(testing.allocator, 10); | |
| 372 | defer b.deinit(); | |
| 373 | ||
| 374 | testing.expect(a.orderAbs(b) == .gt); | |
| 375 | testing.expect(a.order(b) == .lt); | |
| 376 | } | |
| 377 | ||
| 378 | test "big.int compare similar" { | |
| 379 | var a = try Managed.initSet(testing.allocator, 0xffffffffeeeeeeeeffffffffeeeeeeee); | |
| 380 | defer a.deinit(); | |
| 381 | var b = try Managed.initSet(testing.allocator, 0xffffffffeeeeeeeeffffffffeeeeeeef); | |
| 382 | defer b.deinit(); | |
| 383 | ||
| 384 | testing.expect(a.orderAbs(b) == .lt); | |
| 385 | testing.expect(b.orderAbs(a) == .gt); | |
| 386 | } | |
| 387 | ||
| 388 | test "big.int compare different limb size" { | |
| 389 | var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 390 | defer a.deinit(); | |
| 391 | var b = try Managed.initSet(testing.allocator, 1); | |
| 392 | defer b.deinit(); | |
| 393 | ||
| 394 | testing.expect(a.orderAbs(b) == .gt); | |
| 395 | testing.expect(b.orderAbs(a) == .lt); | |
| 396 | } | |
| 397 | ||
| 398 | test "big.int compare multi-limb" { | |
| 399 | var a = try Managed.initSet(testing.allocator, -0x7777777799999999ffffeeeeffffeeeeffffeeeef); | |
| 400 | defer a.deinit(); | |
| 401 | var b = try Managed.initSet(testing.allocator, 0x7777777799999999ffffeeeeffffeeeeffffeeeee); | |
| 402 | defer b.deinit(); | |
| 403 | ||
| 404 | testing.expect(a.orderAbs(b) == .gt); | |
| 405 | testing.expect(a.order(b) == .lt); | |
| 406 | } | |
| 407 | ||
| 408 | test "big.int equality" { | |
| 409 | var a = try Managed.initSet(testing.allocator, 0xffffffff1); | |
| 410 | defer a.deinit(); | |
| 411 | var b = try Managed.initSet(testing.allocator, -0xffffffff1); | |
| 412 | defer b.deinit(); | |
| 413 | ||
| 414 | testing.expect(a.eqAbs(b)); | |
| 415 | testing.expect(!a.eq(b)); | |
| 416 | } | |
| 417 | ||
| 418 | test "big.int abs" { | |
| 419 | var a = try Managed.initSet(testing.allocator, -5); | |
| 420 | defer a.deinit(); | |
| 421 | ||
| 422 | a.abs(); | |
| 423 | testing.expect((try a.to(u32)) == 5); | |
| 424 | ||
| 425 | a.abs(); | |
| 426 | testing.expect((try a.to(u32)) == 5); | |
| 427 | } | |
| 428 | ||
| 429 | test "big.int negate" { | |
| 430 | var a = try Managed.initSet(testing.allocator, 5); | |
| 431 | defer a.deinit(); | |
| 432 | ||
| 433 | a.negate(); | |
| 434 | testing.expect((try a.to(i32)) == -5); | |
| 435 | ||
| 436 | a.negate(); | |
| 437 | testing.expect((try a.to(i32)) == 5); | |
| 438 | } | |
| 439 | ||
| 440 | test "big.int add single-single" { | |
| 441 | var a = try Managed.initSet(testing.allocator, 50); | |
| 442 | defer a.deinit(); | |
| 443 | var b = try Managed.initSet(testing.allocator, 5); | |
| 444 | defer b.deinit(); | |
| 445 | ||
| 446 | var c = try Managed.init(testing.allocator); | |
| 447 | defer c.deinit(); | |
| 448 | try c.add(a.toConst(), b.toConst()); | |
| 449 | ||
| 450 | testing.expect((try c.to(u32)) == 55); | |
| 451 | } | |
| 452 | ||
| 453 | test "big.int add multi-single" { | |
| 454 | var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 455 | defer a.deinit(); | |
| 456 | var b = try Managed.initSet(testing.allocator, 1); | |
| 457 | defer b.deinit(); | |
| 458 | ||
| 459 | var c = try Managed.init(testing.allocator); | |
| 460 | defer c.deinit(); | |
| 461 | ||
| 462 | try c.add(a.toConst(), b.toConst()); | |
| 463 | testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2); | |
| 464 | ||
| 465 | try c.add(b.toConst(), a.toConst()); | |
| 466 | testing.expect((try c.to(DoubleLimb)) == maxInt(Limb) + 2); | |
| 467 | } | |
| 468 | ||
| 469 | test "big.int add multi-multi" { | |
| 470 | const op1 = 0xefefefef7f7f7f7f; | |
| 471 | const op2 = 0xfefefefe9f9f9f9f; | |
| 472 | var a = try Managed.initSet(testing.allocator, op1); | |
| 473 | defer a.deinit(); | |
| 474 | var b = try Managed.initSet(testing.allocator, op2); | |
| 475 | defer b.deinit(); | |
| 476 | ||
| 477 | var c = try Managed.init(testing.allocator); | |
| 478 | defer c.deinit(); | |
| 479 | try c.add(a.toConst(), b.toConst()); | |
| 480 | ||
| 481 | testing.expect((try c.to(u128)) == op1 + op2); | |
| 482 | } | |
| 483 | ||
| 484 | test "big.int add zero-zero" { | |
| 485 | var a = try Managed.initSet(testing.allocator, 0); | |
| 486 | defer a.deinit(); | |
| 487 | var b = try Managed.initSet(testing.allocator, 0); | |
| 488 | defer b.deinit(); | |
| 489 | ||
| 490 | var c = try Managed.init(testing.allocator); | |
| 491 | defer c.deinit(); | |
| 492 | try c.add(a.toConst(), b.toConst()); | |
| 493 | ||
| 494 | testing.expect((try c.to(u32)) == 0); | |
| 495 | } | |
| 496 | ||
| 497 | test "big.int add alias multi-limb nonzero-zero" { | |
| 498 | const op1 = 0xffffffff777777771; | |
| 499 | var a = try Managed.initSet(testing.allocator, op1); | |
| 500 | defer a.deinit(); | |
| 501 | var b = try Managed.initSet(testing.allocator, 0); | |
| 502 | defer b.deinit(); | |
| 503 | ||
| 504 | try a.add(a.toConst(), b.toConst()); | |
| 505 | ||
| 506 | testing.expect((try a.to(u128)) == op1); | |
| 507 | } | |
| 508 | ||
| 509 | test "big.int add sign" { | |
| 510 | var a = try Managed.init(testing.allocator); | |
| 511 | defer a.deinit(); | |
| 512 | ||
| 513 | var one = try Managed.initSet(testing.allocator, 1); | |
| 514 | defer one.deinit(); | |
| 515 | var two = try Managed.initSet(testing.allocator, 2); | |
| 516 | defer two.deinit(); | |
| 517 | var neg_one = try Managed.initSet(testing.allocator, -1); | |
| 518 | defer neg_one.deinit(); | |
| 519 | var neg_two = try Managed.initSet(testing.allocator, -2); | |
| 520 | defer neg_two.deinit(); | |
| 521 | ||
| 522 | try a.add(one.toConst(), two.toConst()); | |
| 523 | testing.expect((try a.to(i32)) == 3); | |
| 524 | ||
| 525 | try a.add(neg_one.toConst(), two.toConst()); | |
| 526 | testing.expect((try a.to(i32)) == 1); | |
| 527 | ||
| 528 | try a.add(one.toConst(), neg_two.toConst()); | |
| 529 | testing.expect((try a.to(i32)) == -1); | |
| 530 | ||
| 531 | try a.add(neg_one.toConst(), neg_two.toConst()); | |
| 532 | testing.expect((try a.to(i32)) == -3); | |
| 533 | } | |
| 534 | ||
| 535 | test "big.int sub single-single" { | |
| 536 | var a = try Managed.initSet(testing.allocator, 50); | |
| 537 | defer a.deinit(); | |
| 538 | var b = try Managed.initSet(testing.allocator, 5); | |
| 539 | defer b.deinit(); | |
| 540 | ||
| 541 | var c = try Managed.init(testing.allocator); | |
| 542 | defer c.deinit(); | |
| 543 | try c.sub(a.toConst(), b.toConst()); | |
| 544 | ||
| 545 | testing.expect((try c.to(u32)) == 45); | |
| 546 | } | |
| 547 | ||
| 548 | test "big.int sub multi-single" { | |
| 549 | var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 550 | defer a.deinit(); | |
| 551 | var b = try Managed.initSet(testing.allocator, 1); | |
| 552 | defer b.deinit(); | |
| 553 | ||
| 554 | var c = try Managed.init(testing.allocator); | |
| 555 | defer c.deinit(); | |
| 556 | try c.sub(a.toConst(), b.toConst()); | |
| 557 | ||
| 558 | testing.expect((try c.to(Limb)) == maxInt(Limb)); | |
| 559 | } | |
| 560 | ||
| 561 | test "big.int sub multi-multi" { | |
| 562 | const op1 = 0xefefefefefefefefefefefef; | |
| 563 | const op2 = 0xabababababababababababab; | |
| 564 | ||
| 565 | var a = try Managed.initSet(testing.allocator, op1); | |
| 566 | defer a.deinit(); | |
| 567 | var b = try Managed.initSet(testing.allocator, op2); | |
| 568 | defer b.deinit(); | |
| 569 | ||
| 570 | var c = try Managed.init(testing.allocator); | |
| 571 | defer c.deinit(); | |
| 572 | try c.sub(a.toConst(), b.toConst()); | |
| 573 | ||
| 574 | testing.expect((try c.to(u128)) == op1 - op2); | |
| 575 | } | |
| 576 | ||
| 577 | test "big.int sub equal" { | |
| 578 | var a = try Managed.initSet(testing.allocator, 0x11efefefefefefefefefefefef); | |
| 579 | defer a.deinit(); | |
| 580 | var b = try Managed.initSet(testing.allocator, 0x11efefefefefefefefefefefef); | |
| 581 | defer b.deinit(); | |
| 582 | ||
| 583 | var c = try Managed.init(testing.allocator); | |
| 584 | defer c.deinit(); | |
| 585 | try c.sub(a.toConst(), b.toConst()); | |
| 586 | ||
| 587 | testing.expect((try c.to(u32)) == 0); | |
| 588 | } | |
| 589 | ||
| 590 | test "big.int sub sign" { | |
| 591 | var a = try Managed.init(testing.allocator); | |
| 592 | defer a.deinit(); | |
| 593 | ||
| 594 | var one = try Managed.initSet(testing.allocator, 1); | |
| 595 | defer one.deinit(); | |
| 596 | var two = try Managed.initSet(testing.allocator, 2); | |
| 597 | defer two.deinit(); | |
| 598 | var neg_one = try Managed.initSet(testing.allocator, -1); | |
| 599 | defer neg_one.deinit(); | |
| 600 | var neg_two = try Managed.initSet(testing.allocator, -2); | |
| 601 | defer neg_two.deinit(); | |
| 602 | ||
| 603 | try a.sub(one.toConst(), two.toConst()); | |
| 604 | testing.expect((try a.to(i32)) == -1); | |
| 605 | ||
| 606 | try a.sub(neg_one.toConst(), two.toConst()); | |
| 607 | testing.expect((try a.to(i32)) == -3); | |
| 608 | ||
| 609 | try a.sub(one.toConst(), neg_two.toConst()); | |
| 610 | testing.expect((try a.to(i32)) == 3); | |
| 611 | ||
| 612 | try a.sub(neg_one.toConst(), neg_two.toConst()); | |
| 613 | testing.expect((try a.to(i32)) == 1); | |
| 614 | ||
| 615 | try a.sub(neg_two.toConst(), neg_one.toConst()); | |
| 616 | testing.expect((try a.to(i32)) == -1); | |
| 617 | } | |
| 618 | ||
| 619 | test "big.int mul single-single" { | |
| 620 | var a = try Managed.initSet(testing.allocator, 50); | |
| 621 | defer a.deinit(); | |
| 622 | var b = try Managed.initSet(testing.allocator, 5); | |
| 623 | defer b.deinit(); | |
| 624 | ||
| 625 | var c = try Managed.init(testing.allocator); | |
| 626 | defer c.deinit(); | |
| 627 | try c.mul(a.toConst(), b.toConst()); | |
| 628 | ||
| 629 | testing.expect((try c.to(u64)) == 250); | |
| 630 | } | |
| 631 | ||
| 632 | test "big.int mul multi-single" { | |
| 633 | var a = try Managed.initSet(testing.allocator, maxInt(Limb)); | |
| 634 | defer a.deinit(); | |
| 635 | var b = try Managed.initSet(testing.allocator, 2); | |
| 636 | defer b.deinit(); | |
| 637 | ||
| 638 | var c = try Managed.init(testing.allocator); | |
| 639 | defer c.deinit(); | |
| 640 | try c.mul(a.toConst(), b.toConst()); | |
| 641 | ||
| 642 | testing.expect((try c.to(DoubleLimb)) == 2 * maxInt(Limb)); | |
| 643 | } | |
| 644 | ||
| 645 | test "big.int mul multi-multi" { | |
| 646 | const op1 = 0x998888efefefefefefefef; | |
| 647 | const op2 = 0x333000abababababababab; | |
| 648 | var a = try Managed.initSet(testing.allocator, op1); | |
| 649 | defer a.deinit(); | |
| 650 | var b = try Managed.initSet(testing.allocator, op2); | |
| 651 | defer b.deinit(); | |
| 652 | ||
| 653 | var c = try Managed.init(testing.allocator); | |
| 654 | defer c.deinit(); | |
| 655 | try c.mul(a.toConst(), b.toConst()); | |
| 656 | ||
| 657 | testing.expect((try c.to(u256)) == op1 * op2); | |
| 658 | } | |
| 659 | ||
| 660 | test "big.int mul alias r with a" { | |
| 661 | var a = try Managed.initSet(testing.allocator, maxInt(Limb)); | |
| 662 | defer a.deinit(); | |
| 663 | var b = try Managed.initSet(testing.allocator, 2); | |
| 664 | defer b.deinit(); | |
| 665 | ||
| 666 | try a.mul(a.toConst(), b.toConst()); | |
| 667 | ||
| 668 | testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb)); | |
| 669 | } | |
| 670 | ||
| 671 | test "big.int mul alias r with b" { | |
| 672 | var a = try Managed.initSet(testing.allocator, maxInt(Limb)); | |
| 673 | defer a.deinit(); | |
| 674 | var b = try Managed.initSet(testing.allocator, 2); | |
| 675 | defer b.deinit(); | |
| 676 | ||
| 677 | try a.mul(b.toConst(), a.toConst()); | |
| 678 | ||
| 679 | testing.expect((try a.to(DoubleLimb)) == 2 * maxInt(Limb)); | |
| 680 | } | |
| 681 | ||
| 682 | test "big.int mul alias r with a and b" { | |
| 683 | var a = try Managed.initSet(testing.allocator, maxInt(Limb)); | |
| 684 | defer a.deinit(); | |
| 685 | ||
| 686 | try a.mul(a.toConst(), a.toConst()); | |
| 687 | ||
| 688 | testing.expect((try a.to(DoubleLimb)) == maxInt(Limb) * maxInt(Limb)); | |
| 689 | } | |
| 690 | ||
| 691 | test "big.int mul a*0" { | |
| 692 | var a = try Managed.initSet(testing.allocator, 0xefefefefefefefef); | |
| 693 | defer a.deinit(); | |
| 694 | var b = try Managed.initSet(testing.allocator, 0); | |
| 695 | defer b.deinit(); | |
| 696 | ||
| 697 | var c = try Managed.init(testing.allocator); | |
| 698 | defer c.deinit(); | |
| 699 | try c.mul(a.toConst(), b.toConst()); | |
| 700 | ||
| 701 | testing.expect((try c.to(u32)) == 0); | |
| 702 | } | |
| 703 | ||
| 704 | test "big.int mul 0*0" { | |
| 705 | var a = try Managed.initSet(testing.allocator, 0); | |
| 706 | defer a.deinit(); | |
| 707 | var b = try Managed.initSet(testing.allocator, 0); | |
| 708 | defer b.deinit(); | |
| 709 | ||
| 710 | var c = try Managed.init(testing.allocator); | |
| 711 | defer c.deinit(); | |
| 712 | try c.mul(a.toConst(), b.toConst()); | |
| 713 | ||
| 714 | testing.expect((try c.to(u32)) == 0); | |
| 715 | } | |
| 716 | ||
| 717 | test "big.int div single-single no rem" { | |
| 718 | var a = try Managed.initSet(testing.allocator, 50); | |
| 719 | defer a.deinit(); | |
| 720 | var b = try Managed.initSet(testing.allocator, 5); | |
| 721 | defer b.deinit(); | |
| 722 | ||
| 723 | var q = try Managed.init(testing.allocator); | |
| 724 | defer q.deinit(); | |
| 725 | var r = try Managed.init(testing.allocator); | |
| 726 | defer r.deinit(); | |
| 727 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 728 | ||
| 729 | testing.expect((try q.to(u32)) == 10); | |
| 730 | testing.expect((try r.to(u32)) == 0); | |
| 731 | } | |
| 732 | ||
| 733 | test "big.int div single-single with rem" { | |
| 734 | var a = try Managed.initSet(testing.allocator, 49); | |
| 735 | defer a.deinit(); | |
| 736 | var b = try Managed.initSet(testing.allocator, 5); | |
| 737 | defer b.deinit(); | |
| 738 | ||
| 739 | var q = try Managed.init(testing.allocator); | |
| 740 | defer q.deinit(); | |
| 741 | var r = try Managed.init(testing.allocator); | |
| 742 | defer r.deinit(); | |
| 743 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 744 | ||
| 745 | testing.expect((try q.to(u32)) == 9); | |
| 746 | testing.expect((try r.to(u32)) == 4); | |
| 747 | } | |
| 748 | ||
| 749 | test "big.int div multi-single no rem" { | |
| 750 | const op1 = 0xffffeeeeddddcccc; | |
| 751 | const op2 = 34; | |
| 752 | ||
| 753 | var a = try Managed.initSet(testing.allocator, op1); | |
| 754 | defer a.deinit(); | |
| 755 | var b = try Managed.initSet(testing.allocator, op2); | |
| 756 | defer b.deinit(); | |
| 757 | ||
| 758 | var q = try Managed.init(testing.allocator); | |
| 759 | defer q.deinit(); | |
| 760 | var r = try Managed.init(testing.allocator); | |
| 761 | defer r.deinit(); | |
| 762 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 763 | ||
| 764 | testing.expect((try q.to(u64)) == op1 / op2); | |
| 765 | testing.expect((try r.to(u64)) == 0); | |
| 766 | } | |
| 767 | ||
| 768 | test "big.int div multi-single with rem" { | |
| 769 | const op1 = 0xffffeeeeddddcccf; | |
| 770 | const op2 = 34; | |
| 771 | ||
| 772 | var a = try Managed.initSet(testing.allocator, op1); | |
| 773 | defer a.deinit(); | |
| 774 | var b = try Managed.initSet(testing.allocator, op2); | |
| 775 | defer b.deinit(); | |
| 776 | ||
| 777 | var q = try Managed.init(testing.allocator); | |
| 778 | defer q.deinit(); | |
| 779 | var r = try Managed.init(testing.allocator); | |
| 780 | defer r.deinit(); | |
| 781 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 782 | ||
| 783 | testing.expect((try q.to(u64)) == op1 / op2); | |
| 784 | testing.expect((try r.to(u64)) == 3); | |
| 785 | } | |
| 786 | ||
| 787 | test "big.int div multi>2-single" { | |
| 788 | const op1 = 0xfefefefefefefefefefefefefefefefe; | |
| 789 | const op2 = 0xefab8; | |
| 790 | ||
| 791 | var a = try Managed.initSet(testing.allocator, op1); | |
| 792 | defer a.deinit(); | |
| 793 | var b = try Managed.initSet(testing.allocator, op2); | |
| 794 | defer b.deinit(); | |
| 795 | ||
| 796 | var q = try Managed.init(testing.allocator); | |
| 797 | defer q.deinit(); | |
| 798 | var r = try Managed.init(testing.allocator); | |
| 799 | defer r.deinit(); | |
| 800 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 801 | ||
| 802 | testing.expect((try q.to(u128)) == op1 / op2); | |
| 803 | testing.expect((try r.to(u32)) == 0x3e4e); | |
| 804 | } | |
| 805 | ||
| 806 | test "big.int div single-single q < r" { | |
| 807 | var a = try Managed.initSet(testing.allocator, 0x0078f432); | |
| 808 | defer a.deinit(); | |
| 809 | var b = try Managed.initSet(testing.allocator, 0x01000000); | |
| 810 | defer b.deinit(); | |
| 811 | ||
| 812 | var q = try Managed.init(testing.allocator); | |
| 813 | defer q.deinit(); | |
| 814 | var r = try Managed.init(testing.allocator); | |
| 815 | defer r.deinit(); | |
| 816 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 817 | ||
| 818 | testing.expect((try q.to(u64)) == 0); | |
| 819 | testing.expect((try r.to(u64)) == 0x0078f432); | |
| 820 | } | |
| 821 | ||
| 822 | test "big.int div single-single q == r" { | |
| 823 | var a = try Managed.initSet(testing.allocator, 10); | |
| 824 | defer a.deinit(); | |
| 825 | var b = try Managed.initSet(testing.allocator, 10); | |
| 826 | defer b.deinit(); | |
| 827 | ||
| 828 | var q = try Managed.init(testing.allocator); | |
| 829 | defer q.deinit(); | |
| 830 | var r = try Managed.init(testing.allocator); | |
| 831 | defer r.deinit(); | |
| 832 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 833 | ||
| 834 | testing.expect((try q.to(u64)) == 1); | |
| 835 | testing.expect((try r.to(u64)) == 0); | |
| 836 | } | |
| 837 | ||
| 838 | test "big.int div q=0 alias" { | |
| 839 | var a = try Managed.initSet(testing.allocator, 3); | |
| 840 | defer a.deinit(); | |
| 841 | var b = try Managed.initSet(testing.allocator, 10); | |
| 842 | defer b.deinit(); | |
| 843 | ||
| 844 | try Managed.divTrunc(&a, &b, a.toConst(), b.toConst()); | |
| 845 | ||
| 846 | testing.expect((try a.to(u64)) == 0); | |
| 847 | testing.expect((try b.to(u64)) == 3); | |
| 848 | } | |
| 849 | ||
| 850 | test "big.int div multi-multi q < r" { | |
| 851 | const op1 = 0x1ffffffff0078f432; | |
| 852 | const op2 = 0x1ffffffff01000000; | |
| 853 | var a = try Managed.initSet(testing.allocator, op1); | |
| 854 | defer a.deinit(); | |
| 855 | var b = try Managed.initSet(testing.allocator, op2); | |
| 856 | defer b.deinit(); | |
| 857 | ||
| 858 | var q = try Managed.init(testing.allocator); | |
| 859 | defer q.deinit(); | |
| 860 | var r = try Managed.init(testing.allocator); | |
| 861 | defer r.deinit(); | |
| 862 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 863 | ||
| 864 | testing.expect((try q.to(u128)) == 0); | |
| 865 | testing.expect((try r.to(u128)) == op1); | |
| 866 | } | |
| 867 | ||
| 868 | test "big.int div trunc single-single +/+" { | |
| 869 | const u: i32 = 5; | |
| 870 | const v: i32 = 3; | |
| 871 | ||
| 872 | var a = try Managed.initSet(testing.allocator, u); | |
| 873 | defer a.deinit(); | |
| 874 | var b = try Managed.initSet(testing.allocator, v); | |
| 875 | defer b.deinit(); | |
| 876 | ||
| 877 | var q = try Managed.init(testing.allocator); | |
| 878 | defer q.deinit(); | |
| 879 | var r = try Managed.init(testing.allocator); | |
| 880 | defer r.deinit(); | |
| 881 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 882 | ||
| 883 | // n = q * d + r | |
| 884 | // 5 = 1 * 3 + 2 | |
| 885 | const eq = @divTrunc(u, v); | |
| 886 | const er = @mod(u, v); | |
| 887 | ||
| 888 | testing.expect((try q.to(i32)) == eq); | |
| 889 | testing.expect((try r.to(i32)) == er); | |
| 890 | } | |
| 891 | ||
| 892 | test "big.int div trunc single-single -/+" { | |
| 893 | const u: i32 = -5; | |
| 894 | const v: i32 = 3; | |
| 895 | ||
| 896 | var a = try Managed.initSet(testing.allocator, u); | |
| 897 | defer a.deinit(); | |
| 898 | var b = try Managed.initSet(testing.allocator, v); | |
| 899 | defer b.deinit(); | |
| 900 | ||
| 901 | var q = try Managed.init(testing.allocator); | |
| 902 | defer q.deinit(); | |
| 903 | var r = try Managed.init(testing.allocator); | |
| 904 | defer r.deinit(); | |
| 905 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 906 | ||
| 907 | // n = q * d + r | |
| 908 | // -5 = 1 * -3 - 2 | |
| 909 | const eq = -1; | |
| 910 | const er = -2; | |
| 911 | ||
| 912 | testing.expect((try q.to(i32)) == eq); | |
| 913 | testing.expect((try r.to(i32)) == er); | |
| 914 | } | |
| 915 | ||
| 916 | test "big.int div trunc single-single +/-" { | |
| 917 | const u: i32 = 5; | |
| 918 | const v: i32 = -3; | |
| 919 | ||
| 920 | var a = try Managed.initSet(testing.allocator, u); | |
| 921 | defer a.deinit(); | |
| 922 | var b = try Managed.initSet(testing.allocator, v); | |
| 923 | defer b.deinit(); | |
| 924 | ||
| 925 | var q = try Managed.init(testing.allocator); | |
| 926 | defer q.deinit(); | |
| 927 | var r = try Managed.init(testing.allocator); | |
| 928 | defer r.deinit(); | |
| 929 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 930 | ||
| 931 | // n = q * d + r | |
| 932 | // 5 = -1 * -3 + 2 | |
| 933 | const eq = -1; | |
| 934 | const er = 2; | |
| 935 | ||
| 936 | testing.expect((try q.to(i32)) == eq); | |
| 937 | testing.expect((try r.to(i32)) == er); | |
| 938 | } | |
| 939 | ||
| 940 | test "big.int div trunc single-single -/-" { | |
| 941 | const u: i32 = -5; | |
| 942 | const v: i32 = -3; | |
| 943 | ||
| 944 | var a = try Managed.initSet(testing.allocator, u); | |
| 945 | defer a.deinit(); | |
| 946 | var b = try Managed.initSet(testing.allocator, v); | |
| 947 | defer b.deinit(); | |
| 948 | ||
| 949 | var q = try Managed.init(testing.allocator); | |
| 950 | defer q.deinit(); | |
| 951 | var r = try Managed.init(testing.allocator); | |
| 952 | defer r.deinit(); | |
| 953 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 954 | ||
| 955 | // n = q * d + r | |
| 956 | // -5 = 1 * -3 - 2 | |
| 957 | const eq = 1; | |
| 958 | const er = -2; | |
| 959 | ||
| 960 | testing.expect((try q.to(i32)) == eq); | |
| 961 | testing.expect((try r.to(i32)) == er); | |
| 962 | } | |
| 963 | ||
| 964 | test "big.int div floor single-single +/+" { | |
| 965 | const u: i32 = 5; | |
| 966 | const v: i32 = 3; | |
| 967 | ||
| 968 | var a = try Managed.initSet(testing.allocator, u); | |
| 969 | defer a.deinit(); | |
| 970 | var b = try Managed.initSet(testing.allocator, v); | |
| 971 | defer b.deinit(); | |
| 972 | ||
| 973 | var q = try Managed.init(testing.allocator); | |
| 974 | defer q.deinit(); | |
| 975 | var r = try Managed.init(testing.allocator); | |
| 976 | defer r.deinit(); | |
| 977 | try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); | |
| 978 | ||
| 979 | // n = q * d + r | |
| 980 | // 5 = 1 * 3 + 2 | |
| 981 | const eq = 1; | |
| 982 | const er = 2; | |
| 983 | ||
| 984 | testing.expect((try q.to(i32)) == eq); | |
| 985 | testing.expect((try r.to(i32)) == er); | |
| 986 | } | |
| 987 | ||
| 988 | test "big.int div floor single-single -/+" { | |
| 989 | const u: i32 = -5; | |
| 990 | const v: i32 = 3; | |
| 991 | ||
| 992 | var a = try Managed.initSet(testing.allocator, u); | |
| 993 | defer a.deinit(); | |
| 994 | var b = try Managed.initSet(testing.allocator, v); | |
| 995 | defer b.deinit(); | |
| 996 | ||
| 997 | var q = try Managed.init(testing.allocator); | |
| 998 | defer q.deinit(); | |
| 999 | var r = try Managed.init(testing.allocator); | |
| 1000 | defer r.deinit(); | |
| 1001 | try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); | |
| 1002 | ||
| 1003 | // n = q * d + r | |
| 1004 | // -5 = -2 * 3 + 1 | |
| 1005 | const eq = -2; | |
| 1006 | const er = 1; | |
| 1007 | ||
| 1008 | testing.expect((try q.to(i32)) == eq); | |
| 1009 | testing.expect((try r.to(i32)) == er); | |
| 1010 | } | |
| 1011 | ||
| 1012 | test "big.int div floor single-single +/-" { | |
| 1013 | const u: i32 = 5; | |
| 1014 | const v: i32 = -3; | |
| 1015 | ||
| 1016 | var a = try Managed.initSet(testing.allocator, u); | |
| 1017 | defer a.deinit(); | |
| 1018 | var b = try Managed.initSet(testing.allocator, v); | |
| 1019 | defer b.deinit(); | |
| 1020 | ||
| 1021 | var q = try Managed.init(testing.allocator); | |
| 1022 | defer q.deinit(); | |
| 1023 | var r = try Managed.init(testing.allocator); | |
| 1024 | defer r.deinit(); | |
| 1025 | try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); | |
| 1026 | ||
| 1027 | // n = q * d + r | |
| 1028 | // 5 = -2 * -3 - 1 | |
| 1029 | const eq = -2; | |
| 1030 | const er = -1; | |
| 1031 | ||
| 1032 | testing.expect((try q.to(i32)) == eq); | |
| 1033 | testing.expect((try r.to(i32)) == er); | |
| 1034 | } | |
| 1035 | ||
| 1036 | test "big.int div floor single-single -/-" { | |
| 1037 | const u: i32 = -5; | |
| 1038 | const v: i32 = -3; | |
| 1039 | ||
| 1040 | var a = try Managed.initSet(testing.allocator, u); | |
| 1041 | defer a.deinit(); | |
| 1042 | var b = try Managed.initSet(testing.allocator, v); | |
| 1043 | defer b.deinit(); | |
| 1044 | ||
| 1045 | var q = try Managed.init(testing.allocator); | |
| 1046 | defer q.deinit(); | |
| 1047 | var r = try Managed.init(testing.allocator); | |
| 1048 | defer r.deinit(); | |
| 1049 | try Managed.divFloor(&q, &r, a.toConst(), b.toConst()); | |
| 1050 | ||
| 1051 | // n = q * d + r | |
| 1052 | // -5 = 2 * -3 + 1 | |
| 1053 | const eq = 1; | |
| 1054 | const er = -2; | |
| 1055 | ||
| 1056 | testing.expect((try q.to(i32)) == eq); | |
| 1057 | testing.expect((try r.to(i32)) == er); | |
| 1058 | } | |
| 1059 | ||
| 1060 | test "big.int div multi-multi with rem" { | |
| 1061 | var a = try Managed.initSet(testing.allocator, 0x8888999911110000ffffeeeeddddccccbbbbaaaa9999); | |
| 1062 | defer a.deinit(); | |
| 1063 | var b = try Managed.initSet(testing.allocator, 0x99990000111122223333); | |
| 1064 | defer b.deinit(); | |
| 1065 | ||
| 1066 | var q = try Managed.init(testing.allocator); | |
| 1067 | defer q.deinit(); | |
| 1068 | var r = try Managed.init(testing.allocator); | |
| 1069 | defer r.deinit(); | |
| 1070 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 1071 | ||
| 1072 | testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); | |
| 1073 | testing.expect((try r.to(u128)) == 0x28de0acacd806823638); | |
| 1074 | } | |
| 1075 | ||
| 1076 | test "big.int div multi-multi no rem" { | |
| 1077 | var a = try Managed.initSet(testing.allocator, 0x8888999911110000ffffeeeedb4fec200ee3a4286361); | |
| 1078 | defer a.deinit(); | |
| 1079 | var b = try Managed.initSet(testing.allocator, 0x99990000111122223333); | |
| 1080 | defer b.deinit(); | |
| 1081 | ||
| 1082 | var q = try Managed.init(testing.allocator); | |
| 1083 | defer q.deinit(); | |
| 1084 | var r = try Managed.init(testing.allocator); | |
| 1085 | defer r.deinit(); | |
| 1086 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 1087 | ||
| 1088 | testing.expect((try q.to(u128)) == 0xe38f38e39161aaabd03f0f1b); | |
| 1089 | testing.expect((try r.to(u128)) == 0); | |
| 1090 | } | |
| 1091 | ||
| 1092 | test "big.int div multi-multi (2 branch)" { | |
| 1093 | var a = try Managed.initSet(testing.allocator, 0x866666665555555588888887777777761111111111111111); | |
| 1094 | defer a.deinit(); | |
| 1095 | var b = try Managed.initSet(testing.allocator, 0x86666666555555554444444433333333); | |
| 1096 | defer b.deinit(); | |
| 1097 | ||
| 1098 | var q = try Managed.init(testing.allocator); | |
| 1099 | defer q.deinit(); | |
| 1100 | var r = try Managed.init(testing.allocator); | |
| 1101 | defer r.deinit(); | |
| 1102 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 1103 | ||
| 1104 | testing.expect((try q.to(u128)) == 0x10000000000000000); | |
| 1105 | testing.expect((try r.to(u128)) == 0x44444443444444431111111111111111); | |
| 1106 | } | |
| 1107 | ||
| 1108 | test "big.int div multi-multi (3.1/3.3 branch)" { | |
| 1109 | var a = try Managed.initSet(testing.allocator, 0x11111111111111111111111111111111111111111111111111111111111111); | |
| 1110 | defer a.deinit(); | |
| 1111 | var b = try Managed.initSet(testing.allocator, 0x1111111111111111111111111111111111111111171); | |
| 1112 | defer b.deinit(); | |
| 1113 | ||
| 1114 | var q = try Managed.init(testing.allocator); | |
| 1115 | defer q.deinit(); | |
| 1116 | var r = try Managed.init(testing.allocator); | |
| 1117 | defer r.deinit(); | |
| 1118 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 1119 | ||
| 1120 | testing.expect((try q.to(u128)) == 0xfffffffffffffffffff); | |
| 1121 | testing.expect((try r.to(u256)) == 0x1111111111111111111110b12222222222222222282); | |
| 1122 | } | |
| 1123 | ||
| 1124 | test "big.int div multi-single zero-limb trailing" { | |
| 1125 | var a = try Managed.initSet(testing.allocator, 0x60000000000000000000000000000000000000000000000000000000000000000); | |
| 1126 | defer a.deinit(); | |
| 1127 | var b = try Managed.initSet(testing.allocator, 0x10000000000000000); | |
| 1128 | defer b.deinit(); | |
| 1129 | ||
| 1130 | var q = try Managed.init(testing.allocator); | |
| 1131 | defer q.deinit(); | |
| 1132 | var r = try Managed.init(testing.allocator); | |
| 1133 | defer r.deinit(); | |
| 1134 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 1135 | ||
| 1136 | var expected = try Managed.initSet(testing.allocator, 0x6000000000000000000000000000000000000000000000000); | |
| 1137 | defer expected.deinit(); | |
| 1138 | testing.expect(q.eq(expected)); | |
| 1139 | testing.expect(r.eqZero()); | |
| 1140 | } | |
| 1141 | ||
| 1142 | test "big.int div multi-multi zero-limb trailing (with rem)" { | |
| 1143 | var a = try Managed.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000); | |
| 1144 | defer a.deinit(); | |
| 1145 | var b = try Managed.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000); | |
| 1146 | defer b.deinit(); | |
| 1147 | ||
| 1148 | var q = try Managed.init(testing.allocator); | |
| 1149 | defer q.deinit(); | |
| 1150 | var r = try Managed.init(testing.allocator); | |
| 1151 | defer r.deinit(); | |
| 1152 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 1153 | ||
| 1154 | testing.expect((try q.to(u128)) == 0x10000000000000000); | |
| 1155 | ||
| 1156 | const rs = try r.toString(testing.allocator, 16, false); | |
| 1157 | defer testing.allocator.free(rs); | |
| 1158 | testing.expect(std.mem.eql(u8, rs, "4444444344444443111111111111111100000000000000000000000000000000")); | |
| 1159 | } | |
| 1160 | ||
| 1161 | test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count > divisor zero-limb count" { | |
| 1162 | var a = try Managed.initSet(testing.allocator, 0x8666666655555555888888877777777611111111111111110000000000000000); | |
| 1163 | defer a.deinit(); | |
| 1164 | var b = try Managed.initSet(testing.allocator, 0x8666666655555555444444443333333300000000000000000000000000000000); | |
| 1165 | defer b.deinit(); | |
| 1166 | ||
| 1167 | var q = try Managed.init(testing.allocator); | |
| 1168 | defer q.deinit(); | |
| 1169 | var r = try Managed.init(testing.allocator); | |
| 1170 | defer r.deinit(); | |
| 1171 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 1172 | ||
| 1173 | testing.expect((try q.to(u128)) == 0x1); | |
| 1174 | ||
| 1175 | const rs = try r.toString(testing.allocator, 16, false); | |
| 1176 | defer testing.allocator.free(rs); | |
| 1177 | testing.expect(std.mem.eql(u8, rs, "444444434444444311111111111111110000000000000000")); | |
| 1178 | } | |
| 1179 | ||
| 1180 | test "big.int div multi-multi zero-limb trailing (with rem) and dividend zero-limb count < divisor zero-limb count" { | |
| 1181 | var a = try Managed.initSet(testing.allocator, 0x86666666555555558888888777777776111111111111111100000000000000000000000000000000); | |
| 1182 | defer a.deinit(); | |
| 1183 | var b = try Managed.initSet(testing.allocator, 0x866666665555555544444444333333330000000000000000); | |
| 1184 | defer b.deinit(); | |
| 1185 | ||
| 1186 | var q = try Managed.init(testing.allocator); | |
| 1187 | defer q.deinit(); | |
| 1188 | var r = try Managed.init(testing.allocator); | |
| 1189 | defer r.deinit(); | |
| 1190 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 1191 | ||
| 1192 | const qs = try q.toString(testing.allocator, 16, false); | |
| 1193 | defer testing.allocator.free(qs); | |
| 1194 | testing.expect(std.mem.eql(u8, qs, "10000000000000000820820803105186f")); | |
| 1195 | ||
| 1196 | const rs = try r.toString(testing.allocator, 16, false); | |
| 1197 | defer testing.allocator.free(rs); | |
| 1198 | testing.expect(std.mem.eql(u8, rs, "4e11f2baa5896a321d463b543d0104e30000000000000000")); | |
| 1199 | } | |
| 1200 | ||
| 1201 | test "big.int div multi-multi fuzz case #1" { | |
| 1202 | var a = try Managed.init(testing.allocator); | |
| 1203 | defer a.deinit(); | |
| 1204 | var b = try Managed.init(testing.allocator); | |
| 1205 | defer b.deinit(); | |
| 1206 | ||
| 1207 | try a.setString(16, "ffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff80000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000003ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffc00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"); | |
| 1208 | try b.setString(16, "3ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffe0000000000000000000000000000000000001ffffffffffffffffffffffffffffffffffffffffffffffffffc000000000000000000000000000000007fffffffffff"); | |
| 1209 | ||
| 1210 | var q = try Managed.init(testing.allocator); | |
| 1211 | defer q.deinit(); | |
| 1212 | var r = try Managed.init(testing.allocator); | |
| 1213 | defer r.deinit(); | |
| 1214 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 1215 | ||
| 1216 | const qs = try q.toString(testing.allocator, 16, false); | |
| 1217 | defer testing.allocator.free(qs); | |
| 1218 | testing.expect(std.mem.eql(u8, qs, "3ffffffffffffffffffffffffffff0000000000000000000000000000000000001ffffffffffffffffffffffffffff7fffffffe000000000000000000000000000180000000000000000000003fffffbfffffffdfffffffffffffeffff800000100101000000100000000020003fffffdfbfffffe3ffffffffffffeffff7fffc00800a100000017ffe000002000400007efbfff7fe9f00000037ffff3fff7fffa004006100000009ffe00000190038200bf7d2ff7fefe80400060000f7d7f8fbf9401fe38e0403ffc0bdffffa51102c300d7be5ef9df4e5060007b0127ad3fa69f97d0f820b6605ff617ddf7f32ad7a05c0d03f2e7bc78a6000e087a8bbcdc59e07a5a079128a7861f553ddebed7e8e56701756f9ead39b48cd1b0831889ea6ec1fddf643d0565b075ff07e6caea4e2854ec9227fd635ed60a2f5eef2893052ffd54718fa08604acbf6a15e78a467c4a3c53c0278af06c4416573f925491b195e8fd79302cb1aaf7caf4ecfc9aec1254cc969786363ac729f914c6ddcc26738d6b0facd54eba026580aba2eb6482a088b0d224a8852420b91ec1")); | |
| 1219 | ||
| 1220 | const rs = try r.toString(testing.allocator, 16, false); | |
| 1221 | defer testing.allocator.free(rs); | |
| 1222 | testing.expect(std.mem.eql(u8, rs, "310d1d4c414426b4836c2635bad1df3a424e50cbdd167ffccb4dfff57d36b4aae0d6ca0910698220171a0f3373c1060a046c2812f0027e321f72979daa5e7973214170d49e885de0c0ecc167837d44502430674a82522e5df6a0759548052420b91ec1")); | |
| 1223 | } | |
| 1224 | ||
| 1225 | test "big.int div multi-multi fuzz case #2" { | |
| 1226 | var a = try Managed.init(testing.allocator); | |
| 1227 | defer a.deinit(); | |
| 1228 | var b = try Managed.init(testing.allocator); | |
| 1229 | defer b.deinit(); | |
| 1230 | ||
| 1231 | try a.setString(16, "3ffffffffe00000000000000000000000000fffffffffffffffffffffffffffffffffffffffffffffffffffffffffe000000000000000000000000000000000000000000000000000000000000001fffffffffffffffff800000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffc000000000000000000000000000000000000000000000000000000000000000"); | |
| 1232 | try b.setString(16, "ffc0000000000000000000000000000000000000000000000000"); | |
| 1233 | ||
| 1234 | var q = try Managed.init(testing.allocator); | |
| 1235 | defer q.deinit(); | |
| 1236 | var r = try Managed.init(testing.allocator); | |
| 1237 | defer r.deinit(); | |
| 1238 | try Managed.divTrunc(&q, &r, a.toConst(), b.toConst()); | |
| 1239 | ||
| 1240 | const qs = try q.toString(testing.allocator, 16, false); | |
| 1241 | defer testing.allocator.free(qs); | |
| 1242 | testing.expect(std.mem.eql(u8, qs, "40100400fe3f8fe3f8fe3f8fe3f8fe3f8fe4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f93e4f91e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4992649926499264991e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4791e4792e4b92e4b92e4b92e4b92a4a92a4a92a4")); | |
| 1243 | ||
| 1244 | const rs = try r.toString(testing.allocator, 16, false); | |
| 1245 | defer testing.allocator.free(rs); | |
| 1246 | testing.expect(std.mem.eql(u8, rs, "a900000000000000000000000000000000000000000000000000")); | |
| 1247 | } | |
| 1248 | ||
| 1249 | test "big.int shift-right single" { | |
| 1250 | var a = try Managed.initSet(testing.allocator, 0xffff0000); | |
| 1251 | defer a.deinit(); | |
| 1252 | try a.shiftRight(a, 16); | |
| 1253 | ||
| 1254 | testing.expect((try a.to(u32)) == 0xffff); | |
| 1255 | } | |
| 1256 | ||
| 1257 | test "big.int shift-right multi" { | |
| 1258 | var a = try Managed.initSet(testing.allocator, 0xffff0000eeee1111dddd2222cccc3333); | |
| 1259 | defer a.deinit(); | |
| 1260 | try a.shiftRight(a, 67); | |
| 1261 | ||
| 1262 | testing.expect((try a.to(u64)) == 0x1fffe0001dddc222); | |
| 1263 | } | |
| 1264 | ||
| 1265 | test "big.int shift-left single" { | |
| 1266 | var a = try Managed.initSet(testing.allocator, 0xffff); | |
| 1267 | defer a.deinit(); | |
| 1268 | try a.shiftLeft(a, 16); | |
| 1269 | ||
| 1270 | testing.expect((try a.to(u64)) == 0xffff0000); | |
| 1271 | } | |
| 1272 | ||
| 1273 | test "big.int shift-left multi" { | |
| 1274 | var a = try Managed.initSet(testing.allocator, 0x1fffe0001dddc222); | |
| 1275 | defer a.deinit(); | |
| 1276 | try a.shiftLeft(a, 67); | |
| 1277 | ||
| 1278 | testing.expect((try a.to(u128)) == 0xffff0000eeee11100000000000000000); | |
| 1279 | } | |
| 1280 | ||
| 1281 | test "big.int shift-right negative" { | |
| 1282 | var a = try Managed.init(testing.allocator); | |
| 1283 | defer a.deinit(); | |
| 1284 | ||
| 1285 | var arg = try Managed.initSet(testing.allocator, -20); | |
| 1286 | defer arg.deinit(); | |
| 1287 | try a.shiftRight(arg, 2); | |
| 1288 | testing.expect((try a.to(i32)) == -20 >> 2); | |
| 1289 | ||
| 1290 | var arg2 = try Managed.initSet(testing.allocator, -5); | |
| 1291 | defer arg2.deinit(); | |
| 1292 | try a.shiftRight(arg2, 10); | |
| 1293 | testing.expect((try a.to(i32)) == -5 >> 10); | |
| 1294 | } | |
| 1295 | ||
| 1296 | test "big.int shift-left negative" { | |
| 1297 | var a = try Managed.init(testing.allocator); | |
| 1298 | defer a.deinit(); | |
| 1299 | ||
| 1300 | var arg = try Managed.initSet(testing.allocator, -10); | |
| 1301 | defer arg.deinit(); | |
| 1302 | try a.shiftRight(arg, 1232); | |
| 1303 | testing.expect((try a.to(i32)) == -10 >> 1232); | |
| 1304 | } | |
| 1305 | ||
| 1306 | test "big.int bitwise and simple" { | |
| 1307 | var a = try Managed.initSet(testing.allocator, 0xffffffff11111111); | |
| 1308 | defer a.deinit(); | |
| 1309 | var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222); | |
| 1310 | defer b.deinit(); | |
| 1311 | ||
| 1312 | try a.bitAnd(a, b); | |
| 1313 | ||
| 1314 | testing.expect((try a.to(u64)) == 0xeeeeeeee00000000); | |
| 1315 | } | |
| 1316 | ||
| 1317 | test "big.int bitwise and multi-limb" { | |
| 1318 | var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 1319 | defer a.deinit(); | |
| 1320 | var b = try Managed.initSet(testing.allocator, maxInt(Limb)); | |
| 1321 | defer b.deinit(); | |
| 1322 | ||
| 1323 | try a.bitAnd(a, b); | |
| 1324 | ||
| 1325 | testing.expect((try a.to(u128)) == 0); | |
| 1326 | } | |
| 1327 | ||
| 1328 | test "big.int bitwise xor simple" { | |
| 1329 | var a = try Managed.initSet(testing.allocator, 0xffffffff11111111); | |
| 1330 | defer a.deinit(); | |
| 1331 | var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222); | |
| 1332 | defer b.deinit(); | |
| 1333 | ||
| 1334 | try a.bitXor(a, b); | |
| 1335 | ||
| 1336 | testing.expect((try a.to(u64)) == 0x1111111133333333); | |
| 1337 | } | |
| 1338 | ||
| 1339 | test "big.int bitwise xor multi-limb" { | |
| 1340 | var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 1341 | defer a.deinit(); | |
| 1342 | var b = try Managed.initSet(testing.allocator, maxInt(Limb)); | |
| 1343 | defer b.deinit(); | |
| 1344 | ||
| 1345 | try a.bitXor(a, b); | |
| 1346 | ||
| 1347 | testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) ^ maxInt(Limb)); | |
| 1348 | } | |
| 1349 | ||
| 1350 | test "big.int bitwise or simple" { | |
| 1351 | var a = try Managed.initSet(testing.allocator, 0xffffffff11111111); | |
| 1352 | defer a.deinit(); | |
| 1353 | var b = try Managed.initSet(testing.allocator, 0xeeeeeeee22222222); | |
| 1354 | defer b.deinit(); | |
| 1355 | ||
| 1356 | try a.bitOr(a, b); | |
| 1357 | ||
| 1358 | testing.expect((try a.to(u64)) == 0xffffffff33333333); | |
| 1359 | } | |
| 1360 | ||
| 1361 | test "big.int bitwise or multi-limb" { | |
| 1362 | var a = try Managed.initSet(testing.allocator, maxInt(Limb) + 1); | |
| 1363 | defer a.deinit(); | |
| 1364 | var b = try Managed.initSet(testing.allocator, maxInt(Limb)); | |
| 1365 | defer b.deinit(); | |
| 1366 | ||
| 1367 | try a.bitOr(a, b); | |
| 1368 | ||
| 1369 | // TODO: big.int.cpp or is wrong on multi-limb. | |
| 1370 | testing.expect((try a.to(DoubleLimb)) == (maxInt(Limb) + 1) + maxInt(Limb)); | |
| 1371 | } | |
| 1372 | ||
| 1373 | test "big.int var args" { | |
| 1374 | var a = try Managed.initSet(testing.allocator, 5); | |
| 1375 | defer a.deinit(); | |
| 1376 | ||
| 1377 | var b = try Managed.initSet(testing.allocator, 6); | |
| 1378 | defer b.deinit(); | |
| 1379 | try a.add(a.toConst(), b.toConst()); | |
| 1380 | testing.expect((try a.to(u64)) == 11); | |
| 1381 | ||
| 1382 | var c = try Managed.initSet(testing.allocator, 11); | |
| 1383 | defer c.deinit(); | |
| 1384 | testing.expect(a.order(c) == .eq); | |
| 1385 | ||
| 1386 | var d = try Managed.initSet(testing.allocator, 14); | |
| 1387 | defer d.deinit(); | |
| 1388 | testing.expect(a.order(d) != .gt); | |
| 1389 | } | |
| 1390 | ||
| 1391 | test "big.int gcd non-one small" { | |
| 1392 | var a = try Managed.initSet(testing.allocator, 17); | |
| 1393 | defer a.deinit(); | |
| 1394 | var b = try Managed.initSet(testing.allocator, 97); | |
| 1395 | defer b.deinit(); | |
| 1396 | var r = try Managed.init(testing.allocator); | |
| 1397 | defer r.deinit(); | |
| 1398 | ||
| 1399 | try r.gcd(a, b); | |
| 1400 | ||
| 1401 | testing.expect((try r.to(u32)) == 1); | |
| 1402 | } | |
| 1403 | ||
| 1404 | test "big.int gcd non-one small" { | |
| 1405 | var a = try Managed.initSet(testing.allocator, 4864); | |
| 1406 | defer a.deinit(); | |
| 1407 | var b = try Managed.initSet(testing.allocator, 3458); | |
| 1408 | defer b.deinit(); | |
| 1409 | var r = try Managed.init(testing.allocator); | |
| 1410 | defer r.deinit(); | |
| 1411 | ||
| 1412 | try r.gcd(a, b); | |
| 1413 | ||
| 1414 | testing.expect((try r.to(u32)) == 38); | |
| 1415 | } | |
| 1416 | ||
| 1417 | test "big.int gcd non-one large" { | |
| 1418 | var a = try Managed.initSet(testing.allocator, 0xffffffffffffffff); | |
| 1419 | defer a.deinit(); | |
| 1420 | var b = try Managed.initSet(testing.allocator, 0xffffffffffffffff7777); | |
| 1421 | defer b.deinit(); | |
| 1422 | var r = try Managed.init(testing.allocator); | |
| 1423 | defer r.deinit(); | |
| 1424 | ||
| 1425 | try r.gcd(a, b); | |
| 1426 | ||
| 1427 | testing.expect((try r.to(u32)) == 4369); | |
| 1428 | } | |
| 1429 | ||
| 1430 | test "big.int gcd large multi-limb result" { | |
| 1431 | var a = try Managed.initSet(testing.allocator, 0x12345678123456781234567812345678123456781234567812345678); | |
| 1432 | defer a.deinit(); | |
| 1433 | var b = try Managed.initSet(testing.allocator, 0x12345671234567123456712345671234567123456712345671234567); | |
| 1434 | defer b.deinit(); | |
| 1435 | var r = try Managed.init(testing.allocator); | |
| 1436 | defer r.deinit(); | |
| 1437 | ||
| 1438 | try r.gcd(a, b); | |
| 1439 | ||
| 1440 | const answer = (try r.to(u256)); | |
| 1441 | testing.expect(answer == 0xf000000ff00000fff0000ffff000fffff00ffffff1); | |
| 1442 | } | |
| 1443 | ||
| 1444 | test "big.int gcd one large" { | |
| 1445 | var a = try Managed.initSet(testing.allocator, 1897056385327307); | |
| 1446 | defer a.deinit(); | |
| 1447 | var b = try Managed.initSet(testing.allocator, 2251799813685248); | |
| 1448 | defer b.deinit(); | |
| 1449 | var r = try Managed.init(testing.allocator); | |
| 1450 | defer r.deinit(); | |
| 1451 | ||
| 1452 | try r.gcd(a, b); | |
| 1453 | ||
| 1454 | testing.expect((try r.to(u64)) == 1); | |
| 1455 | } |
lib/std/math/big/rational.zig+60-57| ... | ... | @@ -5,10 +5,10 @@ const mem = std.mem; |
| 5 | 5 | const testing = std.testing; |
| 6 | 6 | const Allocator = mem.Allocator; |
| 7 | 7 | |
| 8 | const bn = @import("int.zig"); | |
| 9 | const Limb = bn.Limb; | |
| 10 | const DoubleLimb = bn.DoubleLimb; | |
| 11 | const Int = bn.Int; | |
| 8 | const Limb = std.math.big.Limb; | |
| 9 | const DoubleLimb = std.math.big.DoubleLimb; | |
| 10 | const Int = std.math.big.int.Managed; | |
| 11 | const IntConst = std.math.big.int.Const; | |
| 12 | 12 | |
| 13 | 13 | /// An arbitrary-precision rational number. |
| 14 | 14 | /// |
| ... | ... | @@ -17,6 +17,9 @@ const Int = bn.Int; |
| 17 | 17 | /// |
| 18 | 18 | /// Rational's are always normalized. That is, for a Rational r = p/q where p and q are integers, |
| 19 | 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. | |
| 20 | 23 | pub const Rational = struct { |
| 21 | 24 | /// Numerator. Determines the sign of the Rational. |
| 22 | 25 | p: Int, |
| ... | ... | @@ -98,20 +101,20 @@ pub const Rational = struct { |
| 98 | 101 | if (point) |i| { |
| 99 | 102 | try self.p.setString(10, str[0..i]); |
| 100 | 103 | |
| 101 | const base = Int.initFixed(([_]Limb{10})[0..]); | |
| 104 | const base = IntConst{ .limbs = &[_]Limb{10}, .positive = true }; | |
| 102 | 105 | |
| 103 | 106 | var j: usize = start; |
| 104 | 107 | while (j < str.len - i - 1) : (j += 1) { |
| 105 | try self.p.mul(self.p, base); | |
| 108 | try self.p.mul(self.p.toConst(), base); | |
| 106 | 109 | } |
| 107 | 110 | |
| 108 | 111 | try self.q.setString(10, str[i + 1 ..]); |
| 109 | try self.p.add(self.p, self.q); | |
| 112 | try self.p.add(self.p.toConst(), self.q.toConst()); | |
| 110 | 113 | |
| 111 | 114 | try self.q.set(1); |
| 112 | 115 | var k: usize = i + 1; |
| 113 | 116 | while (k < str.len) : (k += 1) { |
| 114 | try self.q.mul(self.q, base); | |
| 117 | try self.q.mul(self.q.toConst(), base); | |
| 115 | 118 | } |
| 116 | 119 | |
| 117 | 120 | try self.reduce(); |
| ... | ... | @@ -218,14 +221,14 @@ pub const Rational = struct { |
| 218 | 221 | } |
| 219 | 222 | |
| 220 | 223 | // 2. compute quotient and remainder |
| 221 | var q = try Int.init(self.p.allocator.?); | |
| 224 | var q = try Int.init(self.p.allocator); | |
| 222 | 225 | defer q.deinit(); |
| 223 | 226 | |
| 224 | 227 | // unused |
| 225 | var r = try Int.init(self.p.allocator.?); | |
| 228 | var r = try Int.init(self.p.allocator); | |
| 226 | 229 | defer r.deinit(); |
| 227 | 230 | |
| 228 | try Int.divTrunc(&q, &r, a2, b2); | |
| 231 | try Int.divTrunc(&q, &r, a2.toConst(), b2.toConst()); | |
| 229 | 232 | |
| 230 | 233 | var mantissa = extractLowBits(q, BitReprType); |
| 231 | 234 | var have_rem = r.len() > 0; |
| ... | ... | @@ -293,14 +296,14 @@ pub const Rational = struct { |
| 293 | 296 | |
| 294 | 297 | /// Set a Rational directly from an Int. |
| 295 | 298 | pub fn copyInt(self: *Rational, a: Int) !void { |
| 296 | try self.p.copy(a); | |
| 299 | try self.p.copy(a.toConst()); | |
| 297 | 300 | try self.q.set(1); |
| 298 | 301 | } |
| 299 | 302 | |
| 300 | 303 | /// Set a Rational directly from a ratio of two Int's. |
| 301 | 304 | pub fn copyRatio(self: *Rational, a: Int, b: Int) !void { |
| 302 | try self.p.copy(a); | |
| 303 | try self.q.copy(b); | |
| 305 | try self.p.copy(a.toConst()); | |
| 306 | try self.q.copy(b.toConst()); | |
| 304 | 307 | |
| 305 | 308 | self.p.setSign(@boolToInt(self.p.isPositive()) ^ @boolToInt(self.q.isPositive()) == 0); |
| 306 | 309 | self.q.setSign(true); |
| ... | ... | @@ -327,13 +330,13 @@ pub const Rational = struct { |
| 327 | 330 | |
| 328 | 331 | /// Returns math.Order.lt, math.Order.eq, math.Order.gt if a < b, a == b or a |
| 329 | 332 | /// > b respectively. |
| 330 | pub fn cmp(a: Rational, b: Rational) !math.Order { | |
| 333 | pub fn order(a: Rational, b: Rational) !math.Order { | |
| 331 | 334 | return cmpInternal(a, b, true); |
| 332 | 335 | } |
| 333 | 336 | |
| 334 | 337 | /// Returns math.Order.lt, math.Order.eq, math.Order.gt if |a| < |b|, |a| == |
| 335 | 338 | /// |b| or |a| > |b| respectively. |
| 336 | pub fn cmpAbs(a: Rational, b: Rational) !math.Order { | |
| 339 | pub fn orderAbs(a: Rational, b: Rational) !math.Order { | |
| 337 | 340 | return cmpInternal(a, b, false); |
| 338 | 341 | } |
| 339 | 342 | |
| ... | ... | @@ -341,16 +344,16 @@ pub const Rational = struct { |
| 341 | 344 | fn cmpInternal(a: Rational, b: Rational, is_abs: bool) !math.Order { |
| 342 | 345 | // TODO: Would a div compare algorithm of sorts be viable and quicker? Can we avoid |
| 343 | 346 | // the memory allocations here? |
| 344 | var q = try Int.init(a.p.allocator.?); | |
| 347 | var q = try Int.init(a.p.allocator); | |
| 345 | 348 | defer q.deinit(); |
| 346 | 349 | |
| 347 | var p = try Int.init(b.p.allocator.?); | |
| 350 | var p = try Int.init(b.p.allocator); | |
| 348 | 351 | defer p.deinit(); |
| 349 | 352 | |
| 350 | try q.mul(a.p, b.q); | |
| 351 | try p.mul(b.p, a.q); | |
| 353 | try q.mul(a.p.toConst(), b.q.toConst()); | |
| 354 | try p.mul(b.p.toConst(), a.q.toConst()); | |
| 352 | 355 | |
| 353 | return if (is_abs) q.cmpAbs(p) else q.cmp(p); | |
| 356 | return if (is_abs) q.orderAbs(p) else q.order(p); | |
| 354 | 357 | } |
| 355 | 358 | |
| 356 | 359 | /// rma = a + b. |
| ... | ... | @@ -364,7 +367,7 @@ pub const Rational = struct { |
| 364 | 367 | |
| 365 | 368 | var sr: Rational = undefined; |
| 366 | 369 | if (aliased) { |
| 367 | sr = try Rational.init(rma.p.allocator.?); | |
| 370 | sr = try Rational.init(rma.p.allocator); | |
| 368 | 371 | r = &sr; |
| 369 | 372 | aliased = true; |
| 370 | 373 | } |
| ... | ... | @@ -373,11 +376,11 @@ pub const Rational = struct { |
| 373 | 376 | r.deinit(); |
| 374 | 377 | }; |
| 375 | 378 | |
| 376 | try r.p.mul(a.p, b.q); | |
| 377 | try r.q.mul(b.p, a.q); | |
| 378 | try r.p.add(r.p, r.q); | |
| 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()); | |
| 379 | 382 | |
| 380 | try r.q.mul(a.q, b.q); | |
| 383 | try r.q.mul(a.q.toConst(), b.q.toConst()); | |
| 381 | 384 | try r.reduce(); |
| 382 | 385 | } |
| 383 | 386 | |
| ... | ... | @@ -392,7 +395,7 @@ pub const Rational = struct { |
| 392 | 395 | |
| 393 | 396 | var sr: Rational = undefined; |
| 394 | 397 | if (aliased) { |
| 395 | sr = try Rational.init(rma.p.allocator.?); | |
| 398 | sr = try Rational.init(rma.p.allocator); | |
| 396 | 399 | r = &sr; |
| 397 | 400 | aliased = true; |
| 398 | 401 | } |
| ... | ... | @@ -401,11 +404,11 @@ pub const Rational = struct { |
| 401 | 404 | r.deinit(); |
| 402 | 405 | }; |
| 403 | 406 | |
| 404 | try r.p.mul(a.p, b.q); | |
| 405 | try r.q.mul(b.p, a.q); | |
| 406 | try r.p.sub(r.p, r.q); | |
| 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()); | |
| 407 | 410 | |
| 408 | try r.q.mul(a.q, b.q); | |
| 411 | try r.q.mul(a.q.toConst(), b.q.toConst()); | |
| 409 | 412 | try r.reduce(); |
| 410 | 413 | } |
| 411 | 414 | |
| ... | ... | @@ -415,8 +418,8 @@ pub const Rational = struct { |
| 415 | 418 | /// |
| 416 | 419 | /// Returns an error if memory could not be allocated. |
| 417 | 420 | pub fn mul(r: *Rational, a: Rational, b: Rational) !void { |
| 418 | try r.p.mul(a.p, b.p); | |
| 419 | try r.q.mul(a.q, b.q); | |
| 421 | try r.p.mul(a.p.toConst(), b.p.toConst()); | |
| 422 | try r.q.mul(a.q.toConst(), b.q.toConst()); | |
| 420 | 423 | try r.reduce(); |
| 421 | 424 | } |
| 422 | 425 | |
| ... | ... | @@ -430,8 +433,8 @@ pub const Rational = struct { |
| 430 | 433 | @panic("division by zero"); |
| 431 | 434 | } |
| 432 | 435 | |
| 433 | try r.p.mul(a.p, b.q); | |
| 434 | try r.q.mul(b.p, a.q); | |
| 436 | try r.p.mul(a.p.toConst(), b.q.toConst()); | |
| 437 | try r.q.mul(b.p.toConst(), a.q.toConst()); | |
| 435 | 438 | try r.reduce(); |
| 436 | 439 | } |
| 437 | 440 | |
| ... | ... | @@ -442,7 +445,7 @@ pub const Rational = struct { |
| 442 | 445 | |
| 443 | 446 | // reduce r/q such that gcd(r, q) = 1 |
| 444 | 447 | fn reduce(r: *Rational) !void { |
| 445 | var a = try Int.init(r.p.allocator.?); | |
| 448 | var a = try Int.init(r.p.allocator); | |
| 446 | 449 | defer a.deinit(); |
| 447 | 450 | |
| 448 | 451 | const sign = r.p.isPositive(); |
| ... | ... | @@ -450,15 +453,15 @@ pub const Rational = struct { |
| 450 | 453 | try a.gcd(r.p, r.q); |
| 451 | 454 | r.p.setSign(sign); |
| 452 | 455 | |
| 453 | const one = Int.initFixed(([_]Limb{1})[0..]); | |
| 454 | if (a.cmp(one) != .eq) { | |
| 455 | var unused = try Int.init(r.p.allocator.?); | |
| 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); | |
| 456 | 459 | defer unused.deinit(); |
| 457 | 460 | |
| 458 | 461 | // TODO: divexact would be useful here |
| 459 | 462 | // TODO: don't copy r.q for div |
| 460 | try Int.divTrunc(&r.p, &unused, r.p, a); | |
| 461 | try Int.divTrunc(&r.q, &unused, r.q, a); | |
| 463 | try Int.divTrunc(&r.p, &unused, r.p.toConst(), a.toConst()); | |
| 464 | try Int.divTrunc(&r.q, &unused, r.q.toConst(), a.toConst()); | |
| 462 | 465 | } |
| 463 | 466 | } |
| 464 | 467 | }; |
| ... | ... | @@ -596,25 +599,25 @@ test "big.rational copy" { |
| 596 | 599 | var a = try Rational.init(testing.allocator); |
| 597 | 600 | defer a.deinit(); |
| 598 | 601 | |
| 599 | const b = try Int.initSet(testing.allocator, 5); | |
| 602 | var b = try Int.initSet(testing.allocator, 5); | |
| 600 | 603 | defer b.deinit(); |
| 601 | 604 | |
| 602 | 605 | try a.copyInt(b); |
| 603 | 606 | testing.expect((try a.p.to(u32)) == 5); |
| 604 | 607 | testing.expect((try a.q.to(u32)) == 1); |
| 605 | 608 | |
| 606 | const c = try Int.initSet(testing.allocator, 7); | |
| 609 | var c = try Int.initSet(testing.allocator, 7); | |
| 607 | 610 | defer c.deinit(); |
| 608 | const d = try Int.initSet(testing.allocator, 3); | |
| 611 | var d = try Int.initSet(testing.allocator, 3); | |
| 609 | 612 | defer d.deinit(); |
| 610 | 613 | |
| 611 | 614 | try a.copyRatio(c, d); |
| 612 | 615 | testing.expect((try a.p.to(u32)) == 7); |
| 613 | 616 | testing.expect((try a.q.to(u32)) == 3); |
| 614 | 617 | |
| 615 | const e = try Int.initSet(testing.allocator, 9); | |
| 618 | var e = try Int.initSet(testing.allocator, 9); | |
| 616 | 619 | defer e.deinit(); |
| 617 | const f = try Int.initSet(testing.allocator, 3); | |
| 620 | var f = try Int.initSet(testing.allocator, 3); | |
| 618 | 621 | defer f.deinit(); |
| 619 | 622 | |
| 620 | 623 | try a.copyRatio(e, f); |
| ... | ... | @@ -680,7 +683,7 @@ test "big.rational swap" { |
| 680 | 683 | testing.expect((try b.q.to(u32)) == 23); |
| 681 | 684 | } |
| 682 | 685 | |
| 683 | test "big.rational cmp" { | |
| 686 | test "big.rational order" { | |
| 684 | 687 | var a = try Rational.init(testing.allocator); |
| 685 | 688 | defer a.deinit(); |
| 686 | 689 | var b = try Rational.init(testing.allocator); |
| ... | ... | @@ -688,11 +691,11 @@ test "big.rational cmp" { |
| 688 | 691 | |
| 689 | 692 | try a.setRatio(500, 231); |
| 690 | 693 | try b.setRatio(18903, 8584); |
| 691 | testing.expect((try a.cmp(b)) == .lt); | |
| 694 | testing.expect((try a.order(b)) == .lt); | |
| 692 | 695 | |
| 693 | 696 | try a.setRatio(890, 10); |
| 694 | 697 | try b.setRatio(89, 1); |
| 695 | testing.expect((try a.cmp(b)) == .eq); | |
| 698 | testing.expect((try a.order(b)) == .eq); | |
| 696 | 699 | } |
| 697 | 700 | |
| 698 | 701 | test "big.rational add single-limb" { |
| ... | ... | @@ -703,11 +706,11 @@ test "big.rational add single-limb" { |
| 703 | 706 | |
| 704 | 707 | try a.setRatio(500, 231); |
| 705 | 708 | try b.setRatio(18903, 8584); |
| 706 | testing.expect((try a.cmp(b)) == .lt); | |
| 709 | testing.expect((try a.order(b)) == .lt); | |
| 707 | 710 | |
| 708 | 711 | try a.setRatio(890, 10); |
| 709 | 712 | try b.setRatio(89, 1); |
| 710 | testing.expect((try a.cmp(b)) == .eq); | |
| 713 | testing.expect((try a.order(b)) == .eq); | |
| 711 | 714 | } |
| 712 | 715 | |
| 713 | 716 | test "big.rational add" { |
| ... | ... | @@ -723,7 +726,7 @@ test "big.rational add" { |
| 723 | 726 | try a.add(a, b); |
| 724 | 727 | |
| 725 | 728 | try r.setRatio(984786924199, 290395044174); |
| 726 | testing.expect((try a.cmp(r)) == .eq); | |
| 729 | testing.expect((try a.order(r)) == .eq); | |
| 727 | 730 | } |
| 728 | 731 | |
| 729 | 732 | test "big.rational sub" { |
| ... | ... | @@ -739,7 +742,7 @@ test "big.rational sub" { |
| 739 | 742 | try a.sub(a, b); |
| 740 | 743 | |
| 741 | 744 | try r.setRatio(979040510045, 290395044174); |
| 742 | testing.expect((try a.cmp(r)) == .eq); | |
| 745 | testing.expect((try a.order(r)) == .eq); | |
| 743 | 746 | } |
| 744 | 747 | |
| 745 | 748 | test "big.rational mul" { |
| ... | ... | @@ -755,7 +758,7 @@ test "big.rational mul" { |
| 755 | 758 | try a.mul(a, b); |
| 756 | 759 | |
| 757 | 760 | try r.setRatio(571481443, 17082061422); |
| 758 | testing.expect((try a.cmp(r)) == .eq); | |
| 761 | testing.expect((try a.order(r)) == .eq); | |
| 759 | 762 | } |
| 760 | 763 | |
| 761 | 764 | test "big.rational div" { |
| ... | ... | @@ -771,7 +774,7 @@ test "big.rational div" { |
| 771 | 774 | try a.div(a, b); |
| 772 | 775 | |
| 773 | 776 | try r.setRatio(75531824394, 221015929); |
| 774 | testing.expect((try a.cmp(r)) == .eq); | |
| 777 | testing.expect((try a.order(r)) == .eq); | |
| 775 | 778 | } |
| 776 | 779 | |
| 777 | 780 | test "big.rational div" { |
| ... | ... | @@ -784,11 +787,11 @@ test "big.rational div" { |
| 784 | 787 | a.invert(); |
| 785 | 788 | |
| 786 | 789 | try r.setRatio(23341, 78923); |
| 787 | testing.expect((try a.cmp(r)) == .eq); | |
| 790 | testing.expect((try a.order(r)) == .eq); | |
| 788 | 791 | |
| 789 | 792 | try a.setRatio(-78923, 23341); |
| 790 | 793 | a.invert(); |
| 791 | 794 | |
| 792 | 795 | try r.setRatio(-23341, 78923); |
| 793 | testing.expect((try a.cmp(r)) == .eq); | |
| 796 | testing.expect((try a.order(r)) == .eq); | |
| 794 | 797 | } |
lib/std/target.zig+1| ... | ... | @@ -404,6 +404,7 @@ pub const Target = struct { |
| 404 | 404 | }; |
| 405 | 405 | |
| 406 | 406 | pub const ObjectFormat = enum { |
| 407 | /// TODO Get rid of this one. | |
| 407 | 408 | unknown, |
| 408 | 409 | coff, |
| 409 | 410 | elf, |
lib/std/testing.zig+39-1| ... | ... | @@ -12,7 +12,7 @@ pub const failing_allocator = &failing_allocator_instance.allocator; |
| 12 | 12 | pub var failing_allocator_instance = FailingAllocator.init(&base_allocator_instance.allocator, 0); |
| 13 | 13 | |
| 14 | 14 | pub var base_allocator_instance = std.heap.ThreadSafeFixedBufferAllocator.init(allocator_mem[0..]); |
| 15 | var allocator_mem: [1024 * 1024]u8 = undefined; | |
| 15 | var allocator_mem: [2 * 1024 * 1024]u8 = undefined; | |
| 16 | 16 | |
| 17 | 17 | /// This function is intended to be used only in tests. It prints diagnostics to stderr |
| 18 | 18 | /// and then aborts when actual_error_union is not expected_error. |
| ... | ... | @@ -193,6 +193,44 @@ pub fn expect(ok: bool) void { |
| 193 | 193 | if (!ok) @panic("test failure"); |
| 194 | 194 | } |
| 195 | 195 | |
| 196 | pub const TmpDir = struct { | |
| 197 | dir: std.fs.Dir, | |
| 198 | parent_dir: std.fs.Dir, | |
| 199 | sub_path: [sub_path_len]u8, | |
| 200 | ||
| 201 | const random_bytes_count = 12; | |
| 202 | const sub_path_len = std.base64.Base64Encoder.calcSize(random_bytes_count); | |
| 203 | ||
| 204 | pub fn cleanup(self: *TmpDir) void { | |
| 205 | self.dir.close(); | |
| 206 | self.parent_dir.deleteTree(&self.sub_path) catch {}; | |
| 207 | self.parent_dir.close(); | |
| 208 | self.* = undefined; | |
| 209 | } | |
| 210 | }; | |
| 211 | ||
| 212 | pub fn tmpDir(opts: std.fs.Dir.OpenDirOptions) TmpDir { | |
| 213 | var random_bytes: [TmpDir.random_bytes_count]u8 = undefined; | |
| 214 | std.crypto.randomBytes(&random_bytes) catch | |
| 215 | @panic("unable to make tmp dir for testing: unable to get random bytes"); | |
| 216 | var sub_path: [TmpDir.sub_path_len]u8 = undefined; | |
| 217 | std.fs.base64_encoder.encode(&sub_path, &random_bytes); | |
| 218 | ||
| 219 | var cache_dir = std.fs.cwd().makeOpenPath("zig-cache", .{}) catch | |
| 220 | @panic("unable to make tmp dir for testing: unable to make and open zig-cache dir"); | |
| 221 | defer cache_dir.close(); | |
| 222 | var parent_dir = cache_dir.makeOpenPath("tmp", .{}) catch | |
| 223 | @panic("unable to make tmp dir for testing: unable to make and open zig-cache/tmp dir"); | |
| 224 | var dir = parent_dir.makeOpenPath(&sub_path, opts) catch | |
| 225 | @panic("unable to make tmp dir for testing: unable to make and open the tmp dir"); | |
| 226 | ||
| 227 | return .{ | |
| 228 | .dir = dir, | |
| 229 | .parent_dir = parent_dir, | |
| 230 | .sub_path = sub_path, | |
| 231 | }; | |
| 232 | } | |
| 233 | ||
| 196 | 234 | test "expectEqual nested array" { |
| 197 | 235 | const a = [2][2]f32{ |
| 198 | 236 | [_]f32{ 1.0, 0.0 }, |
lib/std/zig.zig+17| ... | ... | @@ -9,6 +9,23 @@ pub const ast = @import("zig/ast.zig"); |
| 9 | 9 | pub const system = @import("zig/system.zig"); |
| 10 | 10 | pub const CrossTarget = @import("zig/cross_target.zig").CrossTarget; |
| 11 | 11 | |
| 12 | pub fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { | |
| 13 | var line: usize = 0; | |
| 14 | var column: usize = 0; | |
| 15 | for (source[0..byte_offset]) |byte| { | |
| 16 | switch (byte) { | |
| 17 | '\n' => { | |
| 18 | line += 1; | |
| 19 | column = 0; | |
| 20 | }, | |
| 21 | else => { | |
| 22 | column += 1; | |
| 23 | }, | |
| 24 | } | |
| 25 | } | |
| 26 | return .{ .line = line, .column = column }; | |
| 27 | } | |
| 28 | ||
| 12 | 29 | test "" { |
| 13 | 30 | @import("std").meta.refAllDecls(@This()); |
| 14 | 31 | } |
lib/std/zig/system.zig+6-1| ... | ... | @@ -415,7 +415,12 @@ pub const NativeTargetInfo = struct { |
| 415 | 415 | // over our own shared objects and find a dynamic linker. |
| 416 | 416 | self_exe: { |
| 417 | 417 | const lib_paths = try std.process.getSelfExeSharedLibPaths(allocator); |
| 418 | defer allocator.free(lib_paths); | |
| 418 | defer { | |
| 419 | for (lib_paths) |lib_path| { | |
| 420 | allocator.free(lib_path); | |
| 421 | } | |
| 422 | allocator.free(lib_paths); | |
| 423 | } | |
| 419 | 424 | |
| 420 | 425 | var found_ld_info: LdInfo = undefined; |
| 421 | 426 | var found_ld_path: [:0]const u8 = undefined; |
src-self-hosted/codegen.zig+47-8| ... | ... | @@ -39,7 +39,7 @@ pub fn generateSymbol(typed_value: ir.TypedValue, module: ir.Module, code: *std. |
| 39 | 39 | defer function.inst_table.deinit(); |
| 40 | 40 | defer function.errors.deinit(); |
| 41 | 41 | |
| 42 | for (module_fn.body) |inst| { | |
| 42 | for (module_fn.body.instructions) |inst| { | |
| 43 | 43 | const new_inst = function.genFuncInst(inst) catch |err| switch (err) { |
| 44 | 44 | error.CodegenFail => { |
| 45 | 45 | assert(function.errors.items.len != 0); |
| ... | ... | @@ -77,32 +77,63 @@ const Function = struct { |
| 77 | 77 | |
| 78 | 78 | fn genFuncInst(self: *Function, inst: *ir.Inst) !MCValue { |
| 79 | 79 | switch (inst.tag) { |
| 80 | .unreach => return self.genPanic(inst.src), | |
| 80 | .breakpoint => return self.genBreakpoint(inst.src), | |
| 81 | .unreach => return MCValue{ .unreach = {} }, | |
| 81 | 82 | .constant => unreachable, // excluded from function bodies |
| 82 | 83 | .assembly => return self.genAsm(inst.cast(ir.Inst.Assembly).?), |
| 83 | 84 | .ptrtoint => return self.genPtrToInt(inst.cast(ir.Inst.PtrToInt).?), |
| 84 | 85 | .bitcast => return self.genBitCast(inst.cast(ir.Inst.BitCast).?), |
| 86 | .ret => return self.genRet(inst.cast(ir.Inst.Ret).?), | |
| 87 | .cmp => return self.genCmp(inst.cast(ir.Inst.Cmp).?), | |
| 88 | .condbr => return self.genCondBr(inst.cast(ir.Inst.CondBr).?), | |
| 89 | .isnull => return self.genIsNull(inst.cast(ir.Inst.IsNull).?), | |
| 90 | .isnonnull => return self.genIsNonNull(inst.cast(ir.Inst.IsNonNull).?), | |
| 85 | 91 | } |
| 86 | 92 | } |
| 87 | 93 | |
| 88 | fn genPanic(self: *Function, src: usize) !MCValue { | |
| 89 | // TODO change this to call the panic function | |
| 94 | fn genBreakpoint(self: *Function, src: usize) !MCValue { | |
| 90 | 95 | switch (self.module.target.cpu.arch) { |
| 91 | 96 | .i386, .x86_64 => { |
| 92 | 97 | try self.code.append(0xcc); // int3 |
| 93 | 98 | }, |
| 94 | else => return self.fail(src, "TODO implement panic for {}", .{self.module.target.cpu.arch}), | |
| 99 | else => return self.fail(src, "TODO implement @breakpoint() for {}", .{self.module.target.cpu.arch}), | |
| 95 | 100 | } |
| 96 | 101 | return .unreach; |
| 97 | 102 | } |
| 98 | 103 | |
| 99 | fn genRet(self: *Function, src: usize) !void { | |
| 100 | // TODO change this to call the panic function | |
| 104 | fn genRet(self: *Function, inst: *ir.Inst.Ret) !MCValue { | |
| 101 | 105 | switch (self.module.target.cpu.arch) { |
| 102 | 106 | .i386, .x86_64 => { |
| 103 | 107 | try self.code.append(0xc3); // ret |
| 104 | 108 | }, |
| 105 | else => return self.fail(src, "TODO implement ret for {}", .{self.module.target.cpu.arch}), | |
| 109 | else => return self.fail(inst.base.src, "TODO implement return for {}", .{self.module.target.cpu.arch}), | |
| 110 | } | |
| 111 | return .unreach; | |
| 112 | } | |
| 113 | ||
| 114 | fn genCmp(self: *Function, inst: *ir.Inst.Cmp) !MCValue { | |
| 115 | switch (self.module.target.cpu.arch) { | |
| 116 | else => return self.fail(inst.base.src, "TODO implement cmp for {}", .{self.module.target.cpu.arch}), | |
| 117 | } | |
| 118 | } | |
| 119 | ||
| 120 | fn genCondBr(self: *Function, inst: *ir.Inst.CondBr) !MCValue { | |
| 121 | switch (self.module.target.cpu.arch) { | |
| 122 | else => return self.fail(inst.base.src, "TODO implement condbr for {}", .{self.module.target.cpu.arch}), | |
| 123 | } | |
| 124 | } | |
| 125 | ||
| 126 | fn genIsNull(self: *Function, inst: *ir.Inst.IsNull) !MCValue { | |
| 127 | switch (self.module.target.cpu.arch) { | |
| 128 | else => return self.fail(inst.base.src, "TODO implement isnull for {}", .{self.module.target.cpu.arch}), | |
| 129 | } | |
| 130 | } | |
| 131 | ||
| 132 | fn genIsNonNull(self: *Function, inst: *ir.Inst.IsNonNull) !MCValue { | |
| 133 | // Here you can specialize this instruction if it makes sense to, otherwise the default | |
| 134 | // will call genIsNull and invert the result. | |
| 135 | switch (self.module.target.cpu.arch) { | |
| 136 | else => return self.fail(inst.base.src, "TODO call genIsNull and invert the result ", .{}), | |
| 106 | 137 | } |
| 107 | 138 | } |
| 108 | 139 | |
| ... | ... | @@ -501,11 +532,19 @@ fn Reg(comptime arch: Target.Cpu.Arch) type { |
| 501 | 532 | bh, |
| 502 | 533 | ch, |
| 503 | 534 | dh, |
| 535 | bph, | |
| 536 | sph, | |
| 537 | sih, | |
| 538 | dih, | |
| 504 | 539 | |
| 505 | 540 | al, |
| 506 | 541 | bl, |
| 507 | 542 | cl, |
| 508 | 543 | dl, |
| 544 | bpl, | |
| 545 | spl, | |
| 546 | sil, | |
| 547 | dil, | |
| 509 | 548 | r8b, |
| 510 | 549 | r9b, |
| 511 | 550 | r10b, |
src-self-hosted/ir.zig+665-174| ... | ... | @@ -4,10 +4,12 @@ const Allocator = std.mem.Allocator; |
| 4 | 4 | const Value = @import("value.zig").Value; |
| 5 | 5 | const Type = @import("type.zig").Type; |
| 6 | 6 | const assert = std.debug.assert; |
| 7 | const text = @import("ir/text.zig"); | |
| 8 | const BigInt = std.math.big.Int; | |
| 7 | const BigIntConst = std.math.big.int.Const; | |
| 8 | const BigIntMutable = std.math.big.int.Mutable; | |
| 9 | 9 | const Target = std.Target; |
| 10 | 10 | |
| 11 | pub const text = @import("ir/text.zig"); | |
| 12 | ||
| 11 | 13 | /// These are in-memory, analyzed instructions. See `text.Inst` for the representation |
| 12 | 14 | /// of instructions that correspond to the ZIR text format. |
| 13 | 15 | /// This struct owns the `Value` and `Type` memory. When the struct is deallocated, |
| ... | ... | @@ -20,11 +22,17 @@ pub const Inst = struct { |
| 20 | 22 | src: usize, |
| 21 | 23 | |
| 22 | 24 | pub const Tag = enum { |
| 23 | unreach, | |
| 24 | constant, | |
| 25 | 25 | assembly, |
| 26 | ptrtoint, | |
| 27 | 26 | bitcast, |
| 27 | breakpoint, | |
| 28 | cmp, | |
| 29 | condbr, | |
| 30 | constant, | |
| 31 | isnonnull, | |
| 32 | isnull, | |
| 33 | ptrtoint, | |
| 34 | ret, | |
| 35 | unreach, | |
| 28 | 36 | }; |
| 29 | 37 | |
| 30 | 38 | pub fn cast(base: *Inst, comptime T: type) ?*T { |
| ... | ... | @@ -40,23 +48,64 @@ pub const Inst = struct { |
| 40 | 48 | |
| 41 | 49 | /// Returns `null` if runtime-known. |
| 42 | 50 | pub fn value(base: *Inst) ?Value { |
| 43 | return switch (base.tag) { | |
| 44 | .unreach => Value.initTag(.noreturn_value), | |
| 45 | .constant => base.cast(Constant).?.val, | |
| 46 | ||
| 47 | .assembly, | |
| 48 | .ptrtoint, | |
| 49 | .bitcast, | |
| 50 | => null, | |
| 51 | }; | |
| 51 | if (base.ty.onePossibleValue()) | |
| 52 | return Value.initTag(.the_one_possible_value); | |
| 53 | ||
| 54 | const inst = base.cast(Constant) orelse return null; | |
| 55 | return inst.val; | |
| 52 | 56 | } |
| 53 | 57 | |
| 54 | pub const Unreach = struct { | |
| 55 | pub const base_tag = Tag.unreach; | |
| 58 | pub const Assembly = struct { | |
| 59 | pub const base_tag = Tag.assembly; | |
| 60 | base: Inst, | |
| 61 | ||
| 62 | args: struct { | |
| 63 | asm_source: []const u8, | |
| 64 | is_volatile: bool, | |
| 65 | output: ?[]const u8, | |
| 66 | inputs: []const []const u8, | |
| 67 | clobbers: []const []const u8, | |
| 68 | args: []const *Inst, | |
| 69 | }, | |
| 70 | }; | |
| 71 | ||
| 72 | pub const BitCast = struct { | |
| 73 | pub const base_tag = Tag.bitcast; | |
| 74 | ||
| 75 | base: Inst, | |
| 76 | args: struct { | |
| 77 | operand: *Inst, | |
| 78 | }, | |
| 79 | }; | |
| 80 | ||
| 81 | pub const Breakpoint = struct { | |
| 82 | pub const base_tag = Tag.breakpoint; | |
| 56 | 83 | base: Inst, |
| 57 | 84 | args: void, |
| 58 | 85 | }; |
| 59 | 86 | |
| 87 | pub const Cmp = struct { | |
| 88 | pub const base_tag = Tag.cmp; | |
| 89 | ||
| 90 | base: Inst, | |
| 91 | args: struct { | |
| 92 | lhs: *Inst, | |
| 93 | op: std.math.CompareOperator, | |
| 94 | rhs: *Inst, | |
| 95 | }, | |
| 96 | }; | |
| 97 | ||
| 98 | pub const CondBr = struct { | |
| 99 | pub const base_tag = Tag.condbr; | |
| 100 | ||
| 101 | base: Inst, | |
| 102 | args: struct { | |
| 103 | condition: *Inst, | |
| 104 | true_body: Module.Body, | |
| 105 | false_body: Module.Body, | |
| 106 | }, | |
| 107 | }; | |
| 108 | ||
| 60 | 109 | pub const Constant = struct { |
| 61 | 110 | pub const base_tag = Tag.constant; |
| 62 | 111 | base: Inst, |
| ... | ... | @@ -64,17 +113,21 @@ pub const Inst = struct { |
| 64 | 113 | val: Value, |
| 65 | 114 | }; |
| 66 | 115 | |
| 67 | pub const Assembly = struct { | |
| 68 | pub const base_tag = Tag.assembly; | |
| 116 | pub const IsNonNull = struct { | |
| 117 | pub const base_tag = Tag.isnonnull; | |
| 118 | ||
| 69 | 119 | base: Inst, |
| 120 | args: struct { | |
| 121 | operand: *Inst, | |
| 122 | }, | |
| 123 | }; | |
| 70 | 124 | |
| 125 | pub const IsNull = struct { | |
| 126 | pub const base_tag = Tag.isnull; | |
| 127 | ||
| 128 | base: Inst, | |
| 71 | 129 | args: struct { |
| 72 | asm_source: []const u8, | |
| 73 | is_volatile: bool, | |
| 74 | output: ?[]const u8, | |
| 75 | inputs: []const []const u8, | |
| 76 | clobbers: []const []const u8, | |
| 77 | args: []const *Inst, | |
| 130 | operand: *Inst, | |
| 78 | 131 | }, |
| 79 | 132 | }; |
| 80 | 133 | |
| ... | ... | @@ -87,13 +140,16 @@ pub const Inst = struct { |
| 87 | 140 | }, |
| 88 | 141 | }; |
| 89 | 142 | |
| 90 | pub const BitCast = struct { | |
| 91 | pub const base_tag = Tag.bitcast; | |
| 143 | pub const Ret = struct { | |
| 144 | pub const base_tag = Tag.ret; | |
| 145 | base: Inst, | |
| 146 | args: void, | |
| 147 | }; | |
| 92 | 148 | |
| 149 | pub const Unreach = struct { | |
| 150 | pub const base_tag = Tag.unreach; | |
| 93 | 151 | base: Inst, |
| 94 | args: struct { | |
| 95 | operand: *Inst, | |
| 96 | }, | |
| 152 | args: void, | |
| 97 | 153 | }; |
| 98 | 154 | }; |
| 99 | 155 | |
| ... | ... | @@ -108,6 +164,10 @@ pub const Module = struct { |
| 108 | 164 | arena: std.heap.ArenaAllocator, |
| 109 | 165 | fns: []Fn, |
| 110 | 166 | target: Target, |
| 167 | link_mode: std.builtin.LinkMode, | |
| 168 | output_mode: std.builtin.OutputMode, | |
| 169 | object_format: std.Target.ObjectFormat, | |
| 170 | optimize_mode: std.builtin.Mode, | |
| 111 | 171 | |
| 112 | 172 | pub const Export = struct { |
| 113 | 173 | name: []const u8, |
| ... | ... | @@ -117,13 +177,21 @@ pub const Module = struct { |
| 117 | 177 | |
| 118 | 178 | pub const Fn = struct { |
| 119 | 179 | analysis_status: enum { in_progress, failure, success }, |
| 120 | body: []*Inst, | |
| 180 | body: Body, | |
| 121 | 181 | fn_type: Type, |
| 122 | 182 | }; |
| 123 | 183 | |
| 184 | pub const Body = struct { | |
| 185 | instructions: []*Inst, | |
| 186 | }; | |
| 187 | ||
| 124 | 188 | pub fn deinit(self: *Module, allocator: *Allocator) void { |
| 125 | 189 | allocator.free(self.exports); |
| 126 | 190 | allocator.free(self.errors); |
| 191 | for (self.fns) |f| { | |
| 192 | allocator.free(f.body.instructions); | |
| 193 | } | |
| 194 | allocator.free(self.fns); | |
| 127 | 195 | self.arena.deinit(); |
| 128 | 196 | self.* = undefined; |
| 129 | 197 | } |
| ... | ... | @@ -134,7 +202,15 @@ pub const ErrorMsg = struct { |
| 134 | 202 | msg: []const u8, |
| 135 | 203 | }; |
| 136 | 204 | |
| 137 | pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) !Module { | |
| 205 | pub const AnalyzeOptions = struct { | |
| 206 | target: Target, | |
| 207 | output_mode: std.builtin.OutputMode, | |
| 208 | link_mode: std.builtin.LinkMode, | |
| 209 | object_format: ?std.Target.ObjectFormat = null, | |
| 210 | optimize_mode: std.builtin.Mode, | |
| 211 | }; | |
| 212 | ||
| 213 | pub fn analyze(allocator: *Allocator, old_module: text.Module, options: AnalyzeOptions) !Module { | |
| 138 | 214 | var ctx = Analyze{ |
| 139 | 215 | .allocator = allocator, |
| 140 | 216 | .arena = std.heap.ArenaAllocator.init(allocator), |
| ... | ... | @@ -143,7 +219,10 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) ! |
| 143 | 219 | .decl_table = std.AutoHashMap(*text.Inst, Analyze.NewDecl).init(allocator), |
| 144 | 220 | .exports = std.ArrayList(Module.Export).init(allocator), |
| 145 | 221 | .fns = std.ArrayList(Module.Fn).init(allocator), |
| 146 | .target = target, | |
| 222 | .target = options.target, | |
| 223 | .optimize_mode = options.optimize_mode, | |
| 224 | .link_mode = options.link_mode, | |
| 225 | .output_mode = options.output_mode, | |
| 147 | 226 | }; |
| 148 | 227 | defer ctx.errors.deinit(); |
| 149 | 228 | defer ctx.decl_table.deinit(); |
| ... | ... | @@ -162,7 +241,11 @@ pub fn analyze(allocator: *Allocator, old_module: text.Module, target: Target) ! |
| 162 | 241 | .errors = ctx.errors.toOwnedSlice(), |
| 163 | 242 | .fns = ctx.fns.toOwnedSlice(), |
| 164 | 243 | .arena = ctx.arena, |
| 165 | .target = target, | |
| 244 | .target = ctx.target, | |
| 245 | .link_mode = ctx.link_mode, | |
| 246 | .output_mode = ctx.output_mode, | |
| 247 | .object_format = options.object_format orelse ctx.target.getObjectFormat(), | |
| 248 | .optimize_mode = ctx.optimize_mode, | |
| 166 | 249 | }; |
| 167 | 250 | } |
| 168 | 251 | |
| ... | ... | @@ -175,6 +258,9 @@ const Analyze = struct { |
| 175 | 258 | exports: std.ArrayList(Module.Export), |
| 176 | 259 | fns: std.ArrayList(Module.Fn), |
| 177 | 260 | target: Target, |
| 261 | link_mode: std.builtin.LinkMode, | |
| 262 | optimize_mode: std.builtin.Mode, | |
| 263 | output_mode: std.builtin.OutputMode, | |
| 178 | 264 | |
| 179 | 265 | const NewDecl = struct { |
| 180 | 266 | /// null means a semantic analysis error happened |
| ... | ... | @@ -187,10 +273,15 @@ const Analyze = struct { |
| 187 | 273 | }; |
| 188 | 274 | |
| 189 | 275 | const Fn = struct { |
| 190 | body: std.ArrayList(*Inst), | |
| 191 | inst_table: std.AutoHashMap(*text.Inst, NewInst), | |
| 192 | 276 | /// Index into Module fns array |
| 193 | 277 | fn_index: usize, |
| 278 | inner_block: Block, | |
| 279 | inst_table: std.AutoHashMap(*text.Inst, NewInst), | |
| 280 | }; | |
| 281 | ||
| 282 | const Block = struct { | |
| 283 | func: *Fn, | |
| 284 | instructions: std.ArrayList(*Inst), | |
| 194 | 285 | }; |
| 195 | 286 | |
| 196 | 287 | const InnerError = error{ OutOfMemory, AnalysisFail }; |
| ... | ... | @@ -203,9 +294,9 @@ const Analyze = struct { |
| 203 | 294 | } |
| 204 | 295 | } |
| 205 | 296 | |
| 206 | fn resolveInst(self: *Analyze, opt_func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst { | |
| 207 | if (opt_func) |func| { | |
| 208 | if (func.inst_table.get(old_inst)) |kv| { | |
| 297 | fn resolveInst(self: *Analyze, opt_block: ?*Block, old_inst: *text.Inst) InnerError!*Inst { | |
| 298 | if (opt_block) |block| { | |
| 299 | if (block.func.inst_table.get(old_inst)) |kv| { | |
| 209 | 300 | return kv.value.ptr orelse return error.AnalysisFail; |
| 210 | 301 | } |
| 211 | 302 | } |
| ... | ... | @@ -225,12 +316,12 @@ const Analyze = struct { |
| 225 | 316 | } |
| 226 | 317 | } |
| 227 | 318 | |
| 228 | fn requireFunctionBody(self: *Analyze, func: ?*Fn, src: usize) !*Fn { | |
| 229 | return func orelse return self.fail(src, "instruction illegal outside function body", .{}); | |
| 319 | fn requireRuntimeBlock(self: *Analyze, block: ?*Block, src: usize) !*Block { | |
| 320 | return block orelse return self.fail(src, "instruction illegal outside function body", .{}); | |
| 230 | 321 | } |
| 231 | 322 | |
| 232 | fn resolveInstConst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!TypedValue { | |
| 233 | const new_inst = try self.resolveInst(func, old_inst); | |
| 323 | fn resolveInstConst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!TypedValue { | |
| 324 | const new_inst = try self.resolveInst(block, old_inst); | |
| 234 | 325 | const val = try self.resolveConstValue(new_inst); |
| 235 | 326 | return TypedValue{ |
| 236 | 327 | .ty = new_inst.ty, |
| ... | ... | @@ -239,28 +330,39 @@ const Analyze = struct { |
| 239 | 330 | } |
| 240 | 331 | |
| 241 | 332 | fn resolveConstValue(self: *Analyze, base: *Inst) !Value { |
| 242 | return base.value() orelse return self.fail(base.src, "unable to resolve comptime value", .{}); | |
| 333 | return (try self.resolveDefinedValue(base)) orelse | |
| 334 | return self.fail(base.src, "unable to resolve comptime value", .{}); | |
| 243 | 335 | } |
| 244 | 336 | |
| 245 | fn resolveConstString(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) ![]u8 { | |
| 246 | const new_inst = try self.resolveInst(func, old_inst); | |
| 337 | fn resolveDefinedValue(self: *Analyze, base: *Inst) !?Value { | |
| 338 | if (base.value()) |val| { | |
| 339 | if (val.isUndef()) { | |
| 340 | return self.fail(base.src, "use of undefined value here causes undefined behavior", .{}); | |
| 341 | } | |
| 342 | return val; | |
| 343 | } | |
| 344 | return null; | |
| 345 | } | |
| 346 | ||
| 347 | fn resolveConstString(self: *Analyze, block: ?*Block, old_inst: *text.Inst) ![]u8 { | |
| 348 | const new_inst = try self.resolveInst(block, old_inst); | |
| 247 | 349 | const wanted_type = Type.initTag(.const_slice_u8); |
| 248 | const coerced_inst = try self.coerce(func, wanted_type, new_inst); | |
| 350 | const coerced_inst = try self.coerce(block, wanted_type, new_inst); | |
| 249 | 351 | const val = try self.resolveConstValue(coerced_inst); |
| 250 | 352 | return val.toAllocatedBytes(&self.arena.allocator); |
| 251 | 353 | } |
| 252 | 354 | |
| 253 | fn resolveType(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) !Type { | |
| 254 | const new_inst = try self.resolveInst(func, old_inst); | |
| 355 | fn resolveType(self: *Analyze, block: ?*Block, old_inst: *text.Inst) !Type { | |
| 356 | const new_inst = try self.resolveInst(block, old_inst); | |
| 255 | 357 | const wanted_type = Type.initTag(.@"type"); |
| 256 | const coerced_inst = try self.coerce(func, wanted_type, new_inst); | |
| 358 | const coerced_inst = try self.coerce(block, wanted_type, new_inst); | |
| 257 | 359 | const val = try self.resolveConstValue(coerced_inst); |
| 258 | 360 | return val.toType(); |
| 259 | 361 | } |
| 260 | 362 | |
| 261 | fn analyzeExport(self: *Analyze, func: ?*Fn, export_inst: *text.Inst.Export) !void { | |
| 262 | const symbol_name = try self.resolveConstString(func, export_inst.positionals.symbol_name); | |
| 263 | const typed_value = try self.resolveInstConst(func, export_inst.positionals.value); | |
| 363 | fn analyzeExport(self: *Analyze, block: ?*Block, export_inst: *text.Inst.Export) !void { | |
| 364 | const symbol_name = try self.resolveConstString(block, export_inst.positionals.symbol_name); | |
| 365 | const typed_value = try self.resolveInstConst(block, export_inst.positionals.value); | |
| 264 | 366 | |
| 265 | 367 | switch (typed_value.ty.zigTypeTag()) { |
| 266 | 368 | .Fn => {}, |
| ... | ... | @@ -280,18 +382,18 @@ const Analyze = struct { |
| 280 | 382 | /// TODO should not need the cast on the last parameter at the callsites |
| 281 | 383 | fn addNewInstArgs( |
| 282 | 384 | self: *Analyze, |
| 283 | func: *Fn, | |
| 385 | block: *Block, | |
| 284 | 386 | src: usize, |
| 285 | 387 | ty: Type, |
| 286 | 388 | comptime T: type, |
| 287 | 389 | args: Inst.Args(T), |
| 288 | 390 | ) !*Inst { |
| 289 | const inst = try self.addNewInst(func, src, ty, T); | |
| 391 | const inst = try self.addNewInst(block, src, ty, T); | |
| 290 | 392 | inst.args = args; |
| 291 | 393 | return &inst.base; |
| 292 | 394 | } |
| 293 | 395 | |
| 294 | fn addNewInst(self: *Analyze, func: *Fn, src: usize, ty: Type, comptime T: type) !*T { | |
| 396 | fn addNewInst(self: *Analyze, block: *Block, src: usize, ty: Type, comptime T: type) !*T { | |
| 295 | 397 | const inst = try self.arena.allocator.create(T); |
| 296 | 398 | inst.* = .{ |
| 297 | 399 | .base = .{ |
| ... | ... | @@ -301,7 +403,7 @@ const Analyze = struct { |
| 301 | 403 | }, |
| 302 | 404 | .args = undefined, |
| 303 | 405 | }; |
| 304 | try func.body.append(&inst.base); | |
| 406 | try block.instructions.append(&inst.base); | |
| 305 | 407 | return inst; |
| 306 | 408 | } |
| 307 | 409 | |
| ... | ... | @@ -344,7 +446,21 @@ const Analyze = struct { |
| 344 | 446 | fn constVoid(self: *Analyze, src: usize) !*Inst { |
| 345 | 447 | return self.constInst(src, .{ |
| 346 | 448 | .ty = Type.initTag(.void), |
| 347 | .val = Value.initTag(.void_value), | |
| 449 | .val = Value.initTag(.the_one_possible_value), | |
| 450 | }); | |
| 451 | } | |
| 452 | ||
| 453 | fn constUndef(self: *Analyze, src: usize, ty: Type) !*Inst { | |
| 454 | return self.constInst(src, .{ | |
| 455 | .ty = ty, | |
| 456 | .val = Value.initTag(.undef), | |
| 457 | }); | |
| 458 | } | |
| 459 | ||
| 460 | fn constBool(self: *Analyze, src: usize, v: bool) !*Inst { | |
| 461 | return self.constInst(src, .{ | |
| 462 | .ty = Type.initTag(.bool), | |
| 463 | .val = ([2]Value{ Value.initTag(.bool_false), Value.initTag(.bool_true) })[@boolToInt(v)], | |
| 348 | 464 | }); |
| 349 | 465 | } |
| 350 | 466 | |
| ... | ... | @@ -368,34 +484,38 @@ const Analyze = struct { |
| 368 | 484 | }); |
| 369 | 485 | } |
| 370 | 486 | |
| 371 | fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigInt) !*Inst { | |
| 372 | if (big_int.isPositive()) { | |
| 487 | fn constIntBig(self: *Analyze, src: usize, ty: Type, big_int: BigIntConst) !*Inst { | |
| 488 | const val_payload = if (big_int.positive) blk: { | |
| 373 | 489 | if (big_int.to(u64)) |x| { |
| 374 | 490 | return self.constIntUnsigned(src, ty, x); |
| 375 | 491 | } else |err| switch (err) { |
| 376 | 492 | error.NegativeIntoUnsigned => unreachable, |
| 377 | 493 | error.TargetTooSmall => {}, // handled below |
| 378 | 494 | } |
| 379 | } else { | |
| 495 | const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBigPositive); | |
| 496 | big_int_payload.* = .{ .limbs = big_int.limbs }; | |
| 497 | break :blk &big_int_payload.base; | |
| 498 | } else blk: { | |
| 380 | 499 | if (big_int.to(i64)) |x| { |
| 381 | 500 | return self.constIntSigned(src, ty, x); |
| 382 | 501 | } else |err| switch (err) { |
| 383 | 502 | error.NegativeIntoUnsigned => unreachable, |
| 384 | 503 | error.TargetTooSmall => {}, // handled below |
| 385 | 504 | } |
| 386 | } | |
| 387 | ||
| 388 | const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBig); | |
| 389 | big_int_payload.* = .{ .big_int = big_int }; | |
| 505 | const big_int_payload = try self.arena.allocator.create(Value.Payload.IntBigNegative); | |
| 506 | big_int_payload.* = .{ .limbs = big_int.limbs }; | |
| 507 | break :blk &big_int_payload.base; | |
| 508 | }; | |
| 390 | 509 | |
| 391 | 510 | return self.constInst(src, .{ |
| 392 | 511 | .ty = ty, |
| 393 | .val = Value.initPayload(&big_int_payload.base), | |
| 512 | .val = Value.initPayload(val_payload), | |
| 394 | 513 | }); |
| 395 | 514 | } |
| 396 | 515 | |
| 397 | fn analyzeInst(self: *Analyze, func: ?*Fn, old_inst: *text.Inst) InnerError!*Inst { | |
| 516 | fn analyzeInst(self: *Analyze, block: ?*Block, old_inst: *text.Inst) InnerError!*Inst { | |
| 398 | 517 | switch (old_inst.tag) { |
| 518 | .breakpoint => return self.analyzeInstBreakpoint(block, old_inst.cast(text.Inst.Breakpoint).?), | |
| 399 | 519 | .str => { |
| 400 | 520 | // We can use this reference because Inst.Const's Value is arena-allocated. |
| 401 | 521 | // The value would get copied to a MemoryCell before the `text.Inst.Str` lifetime ends. |
| ... | ... | @@ -406,35 +526,49 @@ const Analyze = struct { |
| 406 | 526 | const big_int = old_inst.cast(text.Inst.Int).?.positionals.int; |
| 407 | 527 | return self.constIntBig(old_inst.src, Type.initTag(.comptime_int), big_int); |
| 408 | 528 | }, |
| 409 | .ptrtoint => return self.analyzeInstPtrToInt(func, old_inst.cast(text.Inst.PtrToInt).?), | |
| 410 | .fieldptr => return self.analyzeInstFieldPtr(func, old_inst.cast(text.Inst.FieldPtr).?), | |
| 411 | .deref => return self.analyzeInstDeref(func, old_inst.cast(text.Inst.Deref).?), | |
| 412 | .as => return self.analyzeInstAs(func, old_inst.cast(text.Inst.As).?), | |
| 413 | .@"asm" => return self.analyzeInstAsm(func, old_inst.cast(text.Inst.Asm).?), | |
| 414 | .@"unreachable" => return self.analyzeInstUnreachable(func, old_inst.cast(text.Inst.Unreachable).?), | |
| 415 | .@"fn" => return self.analyzeInstFn(func, old_inst.cast(text.Inst.Fn).?), | |
| 529 | .ptrtoint => return self.analyzeInstPtrToInt(block, old_inst.cast(text.Inst.PtrToInt).?), | |
| 530 | .fieldptr => return self.analyzeInstFieldPtr(block, old_inst.cast(text.Inst.FieldPtr).?), | |
| 531 | .deref => return self.analyzeInstDeref(block, old_inst.cast(text.Inst.Deref).?), | |
| 532 | .as => return self.analyzeInstAs(block, old_inst.cast(text.Inst.As).?), | |
| 533 | .@"asm" => return self.analyzeInstAsm(block, old_inst.cast(text.Inst.Asm).?), | |
| 534 | .@"unreachable" => return self.analyzeInstUnreachable(block, old_inst.cast(text.Inst.Unreachable).?), | |
| 535 | .@"return" => return self.analyzeInstRet(block, old_inst.cast(text.Inst.Return).?), | |
| 536 | .@"fn" => return self.analyzeInstFn(block, old_inst.cast(text.Inst.Fn).?), | |
| 416 | 537 | .@"export" => { |
| 417 | try self.analyzeExport(func, old_inst.cast(text.Inst.Export).?); | |
| 538 | try self.analyzeExport(block, old_inst.cast(text.Inst.Export).?); | |
| 418 | 539 | return self.constVoid(old_inst.src); |
| 419 | 540 | }, |
| 420 | .primitive => return self.analyzeInstPrimitive(func, old_inst.cast(text.Inst.Primitive).?), | |
| 421 | .fntype => return self.analyzeInstFnType(func, old_inst.cast(text.Inst.FnType).?), | |
| 422 | .intcast => return self.analyzeInstIntCast(func, old_inst.cast(text.Inst.IntCast).?), | |
| 423 | .bitcast => return self.analyzeInstBitCast(func, old_inst.cast(text.Inst.BitCast).?), | |
| 424 | .elemptr => return self.analyzeInstElemPtr(func, old_inst.cast(text.Inst.ElemPtr).?), | |
| 425 | .add => return self.analyzeInstAdd(func, old_inst.cast(text.Inst.Add).?), | |
| 541 | .primitive => return self.analyzeInstPrimitive(old_inst.cast(text.Inst.Primitive).?), | |
| 542 | .fntype => return self.analyzeInstFnType(block, old_inst.cast(text.Inst.FnType).?), | |
| 543 | .intcast => return self.analyzeInstIntCast(block, old_inst.cast(text.Inst.IntCast).?), | |
| 544 | .bitcast => return self.analyzeInstBitCast(block, old_inst.cast(text.Inst.BitCast).?), | |
| 545 | .elemptr => return self.analyzeInstElemPtr(block, old_inst.cast(text.Inst.ElemPtr).?), | |
| 546 | .add => return self.analyzeInstAdd(block, old_inst.cast(text.Inst.Add).?), | |
| 547 | .cmp => return self.analyzeInstCmp(block, old_inst.cast(text.Inst.Cmp).?), | |
| 548 | .condbr => return self.analyzeInstCondBr(block, old_inst.cast(text.Inst.CondBr).?), | |
| 549 | .isnull => return self.analyzeInstIsNull(block, old_inst.cast(text.Inst.IsNull).?), | |
| 550 | .isnonnull => return self.analyzeInstIsNonNull(block, old_inst.cast(text.Inst.IsNonNull).?), | |
| 426 | 551 | } |
| 427 | 552 | } |
| 428 | 553 | |
| 429 | fn analyzeInstFn(self: *Analyze, opt_func: ?*Fn, fn_inst: *text.Inst.Fn) InnerError!*Inst { | |
| 430 | const fn_type = try self.resolveType(opt_func, fn_inst.positionals.fn_type); | |
| 554 | fn analyzeInstBreakpoint(self: *Analyze, block: ?*Block, inst: *text.Inst.Breakpoint) InnerError!*Inst { | |
| 555 | const b = try self.requireRuntimeBlock(block, inst.base.src); | |
| 556 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.void), Inst.Breakpoint, Inst.Args(Inst.Breakpoint){}); | |
| 557 | } | |
| 558 | ||
| 559 | fn analyzeInstFn(self: *Analyze, block: ?*Block, fn_inst: *text.Inst.Fn) InnerError!*Inst { | |
| 560 | const fn_type = try self.resolveType(block, fn_inst.positionals.fn_type); | |
| 431 | 561 | |
| 432 | 562 | var new_func: Fn = .{ |
| 433 | .body = std.ArrayList(*Inst).init(self.allocator), | |
| 434 | .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator), | |
| 435 | 563 | .fn_index = self.fns.items.len, |
| 564 | .inner_block = .{ | |
| 565 | .func = undefined, | |
| 566 | .instructions = std.ArrayList(*Inst).init(self.allocator), | |
| 567 | }, | |
| 568 | .inst_table = std.AutoHashMap(*text.Inst, NewInst).init(self.allocator), | |
| 436 | 569 | }; |
| 437 | defer new_func.body.deinit(); | |
| 570 | new_func.inner_block.func = &new_func; | |
| 571 | defer new_func.inner_block.instructions.deinit(); | |
| 438 | 572 | defer new_func.inst_table.deinit(); |
| 439 | 573 | // Don't hang on to a reference to this when analyzing body instructions, since the memory |
| 440 | 574 | // could become invalid. |
| ... | ... | @@ -444,18 +578,11 @@ const Analyze = struct { |
| 444 | 578 | .body = undefined, |
| 445 | 579 | }; |
| 446 | 580 | |
| 447 | for (fn_inst.positionals.body.instructions) |src_inst| { | |
| 448 | const new_inst = self.analyzeInst(&new_func, src_inst) catch |err| { | |
| 449 | self.fns.items[new_func.fn_index].analysis_status = .failure; | |
| 450 | try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = null }); | |
| 451 | return err; | |
| 452 | }; | |
| 453 | try new_func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst }); | |
| 454 | } | |
| 581 | try self.analyzeBody(&new_func.inner_block, fn_inst.positionals.body); | |
| 455 | 582 | |
| 456 | 583 | const f = &self.fns.items[new_func.fn_index]; |
| 457 | 584 | f.analysis_status = .success; |
| 458 | f.body = new_func.body.toOwnedSlice(); | |
| 585 | f.body = .{ .instructions = new_func.inner_block.instructions.toOwnedSlice() }; | |
| 459 | 586 | |
| 460 | 587 | const fn_payload = try self.arena.allocator.create(Value.Payload.Function); |
| 461 | 588 | fn_payload.* = .{ .index = new_func.fn_index }; |
| ... | ... | @@ -466,8 +593,8 @@ const Analyze = struct { |
| 466 | 593 | }); |
| 467 | 594 | } |
| 468 | 595 | |
| 469 | fn analyzeInstFnType(self: *Analyze, func: ?*Fn, fntype: *text.Inst.FnType) InnerError!*Inst { | |
| 470 | const return_type = try self.resolveType(func, fntype.positionals.return_type); | |
| 596 | fn analyzeInstFnType(self: *Analyze, block: ?*Block, fntype: *text.Inst.FnType) InnerError!*Inst { | |
| 597 | const return_type = try self.resolveType(block, fntype.positionals.return_type); | |
| 471 | 598 | |
| 472 | 599 | if (return_type.zigTypeTag() == .NoReturn and |
| 473 | 600 | fntype.positionals.param_types.len == 0 and |
| ... | ... | @@ -476,33 +603,40 @@ const Analyze = struct { |
| 476 | 603 | return self.constType(fntype.base.src, Type.initTag(.fn_naked_noreturn_no_args)); |
| 477 | 604 | } |
| 478 | 605 | |
| 606 | if (return_type.zigTypeTag() == .Void and | |
| 607 | fntype.positionals.param_types.len == 0 and | |
| 608 | fntype.kw_args.cc == .C) | |
| 609 | { | |
| 610 | return self.constType(fntype.base.src, Type.initTag(.fn_ccc_void_no_args)); | |
| 611 | } | |
| 612 | ||
| 479 | 613 | return self.fail(fntype.base.src, "TODO implement fntype instruction more", .{}); |
| 480 | 614 | } |
| 481 | 615 | |
| 482 | fn analyzeInstPrimitive(self: *Analyze, func: ?*Fn, primitive: *text.Inst.Primitive) InnerError!*Inst { | |
| 616 | fn analyzeInstPrimitive(self: *Analyze, primitive: *text.Inst.Primitive) InnerError!*Inst { | |
| 483 | 617 | return self.constType(primitive.base.src, primitive.positionals.tag.toType()); |
| 484 | 618 | } |
| 485 | 619 | |
| 486 | fn analyzeInstAs(self: *Analyze, func: ?*Fn, as: *text.Inst.As) InnerError!*Inst { | |
| 487 | const dest_type = try self.resolveType(func, as.positionals.dest_type); | |
| 488 | const new_inst = try self.resolveInst(func, as.positionals.value); | |
| 489 | return self.coerce(func, dest_type, new_inst); | |
| 620 | fn analyzeInstAs(self: *Analyze, block: ?*Block, as: *text.Inst.As) InnerError!*Inst { | |
| 621 | const dest_type = try self.resolveType(block, as.positionals.dest_type); | |
| 622 | const new_inst = try self.resolveInst(block, as.positionals.value); | |
| 623 | return self.coerce(block, dest_type, new_inst); | |
| 490 | 624 | } |
| 491 | 625 | |
| 492 | fn analyzeInstPtrToInt(self: *Analyze, func: ?*Fn, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst { | |
| 493 | const ptr = try self.resolveInst(func, ptrtoint.positionals.ptr); | |
| 626 | fn analyzeInstPtrToInt(self: *Analyze, block: ?*Block, ptrtoint: *text.Inst.PtrToInt) InnerError!*Inst { | |
| 627 | const ptr = try self.resolveInst(block, ptrtoint.positionals.ptr); | |
| 494 | 628 | if (ptr.ty.zigTypeTag() != .Pointer) { |
| 495 | 629 | return self.fail(ptrtoint.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}); |
| 496 | 630 | } |
| 497 | 631 | // TODO handle known-pointer-address |
| 498 | const f = try self.requireFunctionBody(func, ptrtoint.base.src); | |
| 632 | const b = try self.requireRuntimeBlock(block, ptrtoint.base.src); | |
| 499 | 633 | const ty = Type.initTag(.usize); |
| 500 | return self.addNewInstArgs(f, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr }); | |
| 634 | return self.addNewInstArgs(b, ptrtoint.base.src, ty, Inst.PtrToInt, Inst.Args(Inst.PtrToInt){ .ptr = ptr }); | |
| 501 | 635 | } |
| 502 | 636 | |
| 503 | fn analyzeInstFieldPtr(self: *Analyze, func: ?*Fn, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst { | |
| 504 | const object_ptr = try self.resolveInst(func, fieldptr.positionals.object_ptr); | |
| 505 | const field_name = try self.resolveConstString(func, fieldptr.positionals.field_name); | |
| 637 | fn analyzeInstFieldPtr(self: *Analyze, block: ?*Block, fieldptr: *text.Inst.FieldPtr) InnerError!*Inst { | |
| 638 | const object_ptr = try self.resolveInst(block, fieldptr.positionals.object_ptr); | |
| 639 | const field_name = try self.resolveConstString(block, fieldptr.positionals.field_name); | |
| 506 | 640 | |
| 507 | 641 | const elem_ty = switch (object_ptr.ty.zigTypeTag()) { |
| 508 | 642 | .Pointer => object_ptr.ty.elemType(), |
| ... | ... | @@ -533,9 +667,9 @@ const Analyze = struct { |
| 533 | 667 | } |
| 534 | 668 | } |
| 535 | 669 | |
| 536 | fn analyzeInstIntCast(self: *Analyze, func: ?*Fn, intcast: *text.Inst.IntCast) InnerError!*Inst { | |
| 537 | const dest_type = try self.resolveType(func, intcast.positionals.dest_type); | |
| 538 | const new_inst = try self.resolveInst(func, intcast.positionals.value); | |
| 670 | fn analyzeInstIntCast(self: *Analyze, block: ?*Block, intcast: *text.Inst.IntCast) InnerError!*Inst { | |
| 671 | const dest_type = try self.resolveType(block, intcast.positionals.dest_type); | |
| 672 | const new_inst = try self.resolveInst(block, intcast.positionals.value); | |
| 539 | 673 | |
| 540 | 674 | const dest_is_comptime_int = switch (dest_type.zigTypeTag()) { |
| 541 | 675 | .ComptimeInt => true, |
| ... | ... | @@ -559,22 +693,22 @@ const Analyze = struct { |
| 559 | 693 | } |
| 560 | 694 | |
| 561 | 695 | if (dest_is_comptime_int or new_inst.value() != null) { |
| 562 | return self.coerce(func, dest_type, new_inst); | |
| 696 | return self.coerce(block, dest_type, new_inst); | |
| 563 | 697 | } |
| 564 | 698 | |
| 565 | 699 | return self.fail(intcast.base.src, "TODO implement analyze widen or shorten int", .{}); |
| 566 | 700 | } |
| 567 | 701 | |
| 568 | fn analyzeInstBitCast(self: *Analyze, func: ?*Fn, inst: *text.Inst.BitCast) InnerError!*Inst { | |
| 569 | const dest_type = try self.resolveType(func, inst.positionals.dest_type); | |
| 570 | const operand = try self.resolveInst(func, inst.positionals.operand); | |
| 571 | return self.bitcast(func, dest_type, operand); | |
| 702 | fn analyzeInstBitCast(self: *Analyze, block: ?*Block, inst: *text.Inst.BitCast) InnerError!*Inst { | |
| 703 | const dest_type = try self.resolveType(block, inst.positionals.dest_type); | |
| 704 | const operand = try self.resolveInst(block, inst.positionals.operand); | |
| 705 | return self.bitcast(block, dest_type, operand); | |
| 572 | 706 | } |
| 573 | 707 | |
| 574 | fn analyzeInstElemPtr(self: *Analyze, func: ?*Fn, inst: *text.Inst.ElemPtr) InnerError!*Inst { | |
| 575 | const array_ptr = try self.resolveInst(func, inst.positionals.array_ptr); | |
| 576 | const uncasted_index = try self.resolveInst(func, inst.positionals.index); | |
| 577 | const elem_index = try self.coerce(func, Type.initTag(.usize), uncasted_index); | |
| 708 | fn analyzeInstElemPtr(self: *Analyze, block: ?*Block, inst: *text.Inst.ElemPtr) InnerError!*Inst { | |
| 709 | const array_ptr = try self.resolveInst(block, inst.positionals.array_ptr); | |
| 710 | const uncasted_index = try self.resolveInst(block, inst.positionals.index); | |
| 711 | const elem_index = try self.coerce(block, Type.initTag(.usize), uncasted_index); | |
| 578 | 712 | |
| 579 | 713 | if (array_ptr.ty.isSinglePointer() and array_ptr.ty.elemType().zigTypeTag() == .Array) { |
| 580 | 714 | if (array_ptr.value()) |array_ptr_val| { |
| ... | ... | @@ -602,28 +736,44 @@ const Analyze = struct { |
| 602 | 736 | return self.fail(inst.base.src, "TODO implement more analyze elemptr", .{}); |
| 603 | 737 | } |
| 604 | 738 | |
| 605 | fn analyzeInstAdd(self: *Analyze, func: ?*Fn, inst: *text.Inst.Add) InnerError!*Inst { | |
| 606 | const lhs = try self.resolveInst(func, inst.positionals.lhs); | |
| 607 | const rhs = try self.resolveInst(func, inst.positionals.rhs); | |
| 739 | fn analyzeInstAdd(self: *Analyze, block: ?*Block, inst: *text.Inst.Add) InnerError!*Inst { | |
| 740 | const lhs = try self.resolveInst(block, inst.positionals.lhs); | |
| 741 | const rhs = try self.resolveInst(block, inst.positionals.rhs); | |
| 608 | 742 | |
| 609 | 743 | if (lhs.ty.zigTypeTag() == .Int and rhs.ty.zigTypeTag() == .Int) { |
| 610 | 744 | if (lhs.value()) |lhs_val| { |
| 611 | 745 | if (rhs.value()) |rhs_val| { |
| 612 | const lhs_bigint = try lhs_val.toBigInt(&self.arena.allocator); | |
| 613 | const rhs_bigint = try rhs_val.toBigInt(&self.arena.allocator); | |
| 614 | var result_bigint = try BigInt.init(&self.arena.allocator); | |
| 615 | try BigInt.add(&result_bigint, lhs_bigint, rhs_bigint); | |
| 746 | // TODO is this a performance issue? maybe we should try the operation without | |
| 747 | // resorting to BigInt first. | |
| 748 | var lhs_space: Value.BigIntSpace = undefined; | |
| 749 | var rhs_space: Value.BigIntSpace = undefined; | |
| 750 | const lhs_bigint = lhs_val.toBigInt(&lhs_space); | |
| 751 | const rhs_bigint = rhs_val.toBigInt(&rhs_space); | |
| 752 | const limbs = try self.arena.allocator.alloc( | |
| 753 | std.math.big.Limb, | |
| 754 | std.math.max(lhs_bigint.limbs.len, rhs_bigint.limbs.len) + 1, | |
| 755 | ); | |
| 756 | var result_bigint = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 757 | result_bigint.add(lhs_bigint, rhs_bigint); | |
| 758 | const result_limbs = result_bigint.limbs[0..result_bigint.len]; | |
| 616 | 759 | |
| 617 | 760 | if (!lhs.ty.eql(rhs.ty)) { |
| 618 | 761 | return self.fail(inst.base.src, "TODO implement peer type resolution", .{}); |
| 619 | 762 | } |
| 620 | 763 | |
| 621 | const val_payload = try self.arena.allocator.create(Value.Payload.IntBig); | |
| 622 | val_payload.* = .{ .big_int = result_bigint }; | |
| 764 | const val_payload = if (result_bigint.positive) blk: { | |
| 765 | const val_payload = try self.arena.allocator.create(Value.Payload.IntBigPositive); | |
| 766 | val_payload.* = .{ .limbs = result_limbs }; | |
| 767 | break :blk &val_payload.base; | |
| 768 | } else blk: { | |
| 769 | const val_payload = try self.arena.allocator.create(Value.Payload.IntBigNegative); | |
| 770 | val_payload.* = .{ .limbs = result_limbs }; | |
| 771 | break :blk &val_payload.base; | |
| 772 | }; | |
| 623 | 773 | |
| 624 | 774 | return self.constInst(inst.base.src, .{ |
| 625 | 775 | .ty = lhs.ty, |
| 626 | .val = Value.initPayload(&val_payload.base), | |
| 776 | .val = Value.initPayload(val_payload), | |
| 627 | 777 | }); |
| 628 | 778 | } |
| 629 | 779 | } |
| ... | ... | @@ -632,8 +782,8 @@ const Analyze = struct { |
| 632 | 782 | return self.fail(inst.base.src, "TODO implement more analyze add", .{}); |
| 633 | 783 | } |
| 634 | 784 | |
| 635 | fn analyzeInstDeref(self: *Analyze, func: ?*Fn, deref: *text.Inst.Deref) InnerError!*Inst { | |
| 636 | const ptr = try self.resolveInst(func, deref.positionals.ptr); | |
| 785 | fn analyzeInstDeref(self: *Analyze, block: ?*Block, deref: *text.Inst.Deref) InnerError!*Inst { | |
| 786 | const ptr = try self.resolveInst(block, deref.positionals.ptr); | |
| 637 | 787 | const elem_ty = switch (ptr.ty.zigTypeTag()) { |
| 638 | 788 | .Pointer => ptr.ty.elemType(), |
| 639 | 789 | else => return self.fail(deref.positionals.ptr.src, "expected pointer, found '{}'", .{ptr.ty}), |
| ... | ... | @@ -648,28 +798,28 @@ const Analyze = struct { |
| 648 | 798 | return self.fail(deref.base.src, "TODO implement runtime deref", .{}); |
| 649 | 799 | } |
| 650 | 800 | |
| 651 | fn analyzeInstAsm(self: *Analyze, func: ?*Fn, assembly: *text.Inst.Asm) InnerError!*Inst { | |
| 652 | const return_type = try self.resolveType(func, assembly.positionals.return_type); | |
| 653 | const asm_source = try self.resolveConstString(func, assembly.positionals.asm_source); | |
| 654 | const output = if (assembly.kw_args.output) |o| try self.resolveConstString(func, o) else null; | |
| 801 | fn analyzeInstAsm(self: *Analyze, block: ?*Block, assembly: *text.Inst.Asm) InnerError!*Inst { | |
| 802 | const return_type = try self.resolveType(block, assembly.positionals.return_type); | |
| 803 | const asm_source = try self.resolveConstString(block, assembly.positionals.asm_source); | |
| 804 | const output = if (assembly.kw_args.output) |o| try self.resolveConstString(block, o) else null; | |
| 655 | 805 | |
| 656 | 806 | const inputs = try self.arena.allocator.alloc([]const u8, assembly.kw_args.inputs.len); |
| 657 | 807 | const clobbers = try self.arena.allocator.alloc([]const u8, assembly.kw_args.clobbers.len); |
| 658 | 808 | const args = try self.arena.allocator.alloc(*Inst, assembly.kw_args.args.len); |
| 659 | 809 | |
| 660 | 810 | for (inputs) |*elem, i| { |
| 661 | elem.* = try self.resolveConstString(func, assembly.kw_args.inputs[i]); | |
| 811 | elem.* = try self.resolveConstString(block, assembly.kw_args.inputs[i]); | |
| 662 | 812 | } |
| 663 | 813 | for (clobbers) |*elem, i| { |
| 664 | elem.* = try self.resolveConstString(func, assembly.kw_args.clobbers[i]); | |
| 814 | elem.* = try self.resolveConstString(block, assembly.kw_args.clobbers[i]); | |
| 665 | 815 | } |
| 666 | 816 | for (args) |*elem, i| { |
| 667 | const arg = try self.resolveInst(func, assembly.kw_args.args[i]); | |
| 668 | elem.* = try self.coerce(func, Type.initTag(.usize), arg); | |
| 817 | const arg = try self.resolveInst(block, assembly.kw_args.args[i]); | |
| 818 | elem.* = try self.coerce(block, Type.initTag(.usize), arg); | |
| 669 | 819 | } |
| 670 | 820 | |
| 671 | const f = try self.requireFunctionBody(func, assembly.base.src); | |
| 672 | return self.addNewInstArgs(f, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){ | |
| 821 | const b = try self.requireRuntimeBlock(block, assembly.base.src); | |
| 822 | return self.addNewInstArgs(b, assembly.base.src, return_type, Inst.Assembly, Inst.Args(Inst.Assembly){ | |
| 673 | 823 | .asm_source = asm_source, |
| 674 | 824 | .is_volatile = assembly.kw_args.@"volatile", |
| 675 | 825 | .output = output, |
| ... | ... | @@ -679,19 +829,370 @@ const Analyze = struct { |
| 679 | 829 | }); |
| 680 | 830 | } |
| 681 | 831 | |
| 682 | fn analyzeInstUnreachable(self: *Analyze, func: ?*Fn, unreach: *text.Inst.Unreachable) InnerError!*Inst { | |
| 683 | const f = try self.requireFunctionBody(func, unreach.base.src); | |
| 684 | return self.addNewInstArgs(f, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {}); | |
| 832 | fn analyzeInstCmp(self: *Analyze, block: ?*Block, inst: *text.Inst.Cmp) InnerError!*Inst { | |
| 833 | const lhs = try self.resolveInst(block, inst.positionals.lhs); | |
| 834 | const rhs = try self.resolveInst(block, inst.positionals.rhs); | |
| 835 | const op = inst.positionals.op; | |
| 836 | ||
| 837 | const is_equality_cmp = switch (op) { | |
| 838 | .eq, .neq => true, | |
| 839 | else => false, | |
| 840 | }; | |
| 841 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | |
| 842 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | |
| 843 | if (is_equality_cmp and lhs_ty_tag == .Null and rhs_ty_tag == .Null) { | |
| 844 | // null == null, null != null | |
| 845 | return self.constBool(inst.base.src, op == .eq); | |
| 846 | } else if (is_equality_cmp and | |
| 847 | ((lhs_ty_tag == .Null and rhs_ty_tag == .Optional) or | |
| 848 | rhs_ty_tag == .Null and lhs_ty_tag == .Optional)) | |
| 849 | { | |
| 850 | // comparing null with optionals | |
| 851 | const opt_operand = if (lhs_ty_tag == .Optional) lhs else rhs; | |
| 852 | if (opt_operand.value()) |opt_val| { | |
| 853 | const is_null = opt_val.isNull(); | |
| 854 | return self.constBool(inst.base.src, if (op == .eq) is_null else !is_null); | |
| 855 | } | |
| 856 | const b = try self.requireRuntimeBlock(block, inst.base.src); | |
| 857 | switch (op) { | |
| 858 | .eq => return self.addNewInstArgs( | |
| 859 | b, | |
| 860 | inst.base.src, | |
| 861 | Type.initTag(.bool), | |
| 862 | Inst.IsNull, | |
| 863 | Inst.Args(Inst.IsNull){ .operand = opt_operand }, | |
| 864 | ), | |
| 865 | .neq => return self.addNewInstArgs( | |
| 866 | b, | |
| 867 | inst.base.src, | |
| 868 | Type.initTag(.bool), | |
| 869 | Inst.IsNonNull, | |
| 870 | Inst.Args(Inst.IsNonNull){ .operand = opt_operand }, | |
| 871 | ), | |
| 872 | else => unreachable, | |
| 873 | } | |
| 874 | } else if (is_equality_cmp and | |
| 875 | ((lhs_ty_tag == .Null and rhs.ty.isCPtr()) or (rhs_ty_tag == .Null and lhs.ty.isCPtr()))) | |
| 876 | { | |
| 877 | return self.fail(inst.base.src, "TODO implement C pointer cmp", .{}); | |
| 878 | } else if (lhs_ty_tag == .Null or rhs_ty_tag == .Null) { | |
| 879 | const non_null_type = if (lhs_ty_tag == .Null) rhs.ty else lhs.ty; | |
| 880 | return self.fail(inst.base.src, "comparison of '{}' with null", .{non_null_type}); | |
| 881 | } else if (is_equality_cmp and | |
| 882 | ((lhs_ty_tag == .EnumLiteral and rhs_ty_tag == .Union) or | |
| 883 | (rhs_ty_tag == .EnumLiteral and lhs_ty_tag == .Union))) | |
| 884 | { | |
| 885 | return self.fail(inst.base.src, "TODO implement equality comparison between a union's tag value and an enum literal", .{}); | |
| 886 | } else if (lhs_ty_tag == .ErrorSet and rhs_ty_tag == .ErrorSet) { | |
| 887 | if (!is_equality_cmp) { | |
| 888 | return self.fail(inst.base.src, "{} operator not allowed for errors", .{@tagName(op)}); | |
| 889 | } | |
| 890 | return self.fail(inst.base.src, "TODO implement equality comparison between errors", .{}); | |
| 891 | } else if (lhs.ty.isNumeric() and rhs.ty.isNumeric()) { | |
| 892 | // This operation allows any combination of integer and float types, regardless of the | |
| 893 | // signed-ness, comptime-ness, and bit-width. So peer type resolution is incorrect for | |
| 894 | // numeric types. | |
| 895 | return self.cmpNumeric(block, inst.base.src, lhs, rhs, op); | |
| 896 | } | |
| 897 | return self.fail(inst.base.src, "TODO implement more cmp analysis", .{}); | |
| 898 | } | |
| 899 | ||
| 900 | fn analyzeInstIsNull(self: *Analyze, block: ?*Block, inst: *text.Inst.IsNull) InnerError!*Inst { | |
| 901 | const operand = try self.resolveInst(block, inst.positionals.operand); | |
| 902 | return self.analyzeIsNull(block, inst.base.src, operand, true); | |
| 903 | } | |
| 904 | ||
| 905 | fn analyzeInstIsNonNull(self: *Analyze, block: ?*Block, inst: *text.Inst.IsNonNull) InnerError!*Inst { | |
| 906 | const operand = try self.resolveInst(block, inst.positionals.operand); | |
| 907 | return self.analyzeIsNull(block, inst.base.src, operand, false); | |
| 908 | } | |
| 909 | ||
| 910 | fn analyzeInstCondBr(self: *Analyze, block: ?*Block, inst: *text.Inst.CondBr) InnerError!*Inst { | |
| 911 | const uncasted_cond = try self.resolveInst(block, inst.positionals.condition); | |
| 912 | const cond = try self.coerce(block, Type.initTag(.bool), uncasted_cond); | |
| 913 | ||
| 914 | if (try self.resolveDefinedValue(cond)) |cond_val| { | |
| 915 | const body = if (cond_val.toBool()) &inst.positionals.true_body else &inst.positionals.false_body; | |
| 916 | try self.analyzeBody(block, body.*); | |
| 917 | return self.constVoid(inst.base.src); | |
| 918 | } | |
| 919 | ||
| 920 | const parent_block = try self.requireRuntimeBlock(block, inst.base.src); | |
| 921 | ||
| 922 | var true_block: Block = .{ | |
| 923 | .func = parent_block.func, | |
| 924 | .instructions = std.ArrayList(*Inst).init(self.allocator), | |
| 925 | }; | |
| 926 | defer true_block.instructions.deinit(); | |
| 927 | try self.analyzeBody(&true_block, inst.positionals.true_body); | |
| 928 | ||
| 929 | var false_block: Block = .{ | |
| 930 | .func = parent_block.func, | |
| 931 | .instructions = std.ArrayList(*Inst).init(self.allocator), | |
| 932 | }; | |
| 933 | defer false_block.instructions.deinit(); | |
| 934 | try self.analyzeBody(&false_block, inst.positionals.false_body); | |
| 935 | ||
| 936 | // Copy the instruction pointers to the arena memory | |
| 937 | const true_instructions = try self.arena.allocator.alloc(*Inst, true_block.instructions.items.len); | |
| 938 | const false_instructions = try self.arena.allocator.alloc(*Inst, false_block.instructions.items.len); | |
| 939 | ||
| 940 | mem.copy(*Inst, true_instructions, true_block.instructions.items); | |
| 941 | mem.copy(*Inst, false_instructions, false_block.instructions.items); | |
| 942 | ||
| 943 | return self.addNewInstArgs(parent_block, inst.base.src, Type.initTag(.void), Inst.CondBr, Inst.Args(Inst.CondBr){ | |
| 944 | .condition = cond, | |
| 945 | .true_body = .{ .instructions = true_instructions }, | |
| 946 | .false_body = .{ .instructions = false_instructions }, | |
| 947 | }); | |
| 948 | } | |
| 949 | ||
| 950 | fn wantSafety(self: *Analyze, block: ?*Block) bool { | |
| 951 | return switch (self.optimize_mode) { | |
| 952 | .Debug => true, | |
| 953 | .ReleaseSafe => true, | |
| 954 | .ReleaseFast => false, | |
| 955 | .ReleaseSmall => false, | |
| 956 | }; | |
| 957 | } | |
| 958 | ||
| 959 | fn analyzeInstUnreachable(self: *Analyze, block: ?*Block, unreach: *text.Inst.Unreachable) InnerError!*Inst { | |
| 960 | const b = try self.requireRuntimeBlock(block, unreach.base.src); | |
| 961 | if (self.wantSafety(block)) { | |
| 962 | // TODO Once we have a panic function to call, call it here instead of this. | |
| 963 | _ = try self.addNewInstArgs(b, unreach.base.src, Type.initTag(.void), Inst.Breakpoint, {}); | |
| 964 | } | |
| 965 | return self.addNewInstArgs(b, unreach.base.src, Type.initTag(.noreturn), Inst.Unreach, {}); | |
| 966 | } | |
| 967 | ||
| 968 | fn analyzeInstRet(self: *Analyze, block: ?*Block, inst: *text.Inst.Return) InnerError!*Inst { | |
| 969 | const b = try self.requireRuntimeBlock(block, inst.base.src); | |
| 970 | return self.addNewInstArgs(b, inst.base.src, Type.initTag(.noreturn), Inst.Ret, {}); | |
| 971 | } | |
| 972 | ||
| 973 | fn analyzeBody(self: *Analyze, block: ?*Block, body: text.Module.Body) !void { | |
| 974 | for (body.instructions) |src_inst| { | |
| 975 | const new_inst = self.analyzeInst(block, src_inst) catch |err| { | |
| 976 | if (block) |b| { | |
| 977 | self.fns.items[b.func.fn_index].analysis_status = .failure; | |
| 978 | try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = null }); | |
| 979 | } | |
| 980 | return err; | |
| 981 | }; | |
| 982 | if (block) |b| try b.func.inst_table.putNoClobber(src_inst, .{ .ptr = new_inst }); | |
| 983 | } | |
| 984 | } | |
| 985 | ||
| 986 | fn analyzeIsNull( | |
| 987 | self: *Analyze, | |
| 988 | block: ?*Block, | |
| 989 | src: usize, | |
| 990 | operand: *Inst, | |
| 991 | invert_logic: bool, | |
| 992 | ) InnerError!*Inst { | |
| 993 | return self.fail(src, "TODO implement analysis of isnull and isnotnull", .{}); | |
| 994 | } | |
| 995 | ||
| 996 | /// Asserts that lhs and rhs types are both numeric. | |
| 997 | fn cmpNumeric( | |
| 998 | self: *Analyze, | |
| 999 | block: ?*Block, | |
| 1000 | src: usize, | |
| 1001 | lhs: *Inst, | |
| 1002 | rhs: *Inst, | |
| 1003 | op: std.math.CompareOperator, | |
| 1004 | ) !*Inst { | |
| 1005 | assert(lhs.ty.isNumeric()); | |
| 1006 | assert(rhs.ty.isNumeric()); | |
| 1007 | ||
| 1008 | const lhs_ty_tag = lhs.ty.zigTypeTag(); | |
| 1009 | const rhs_ty_tag = rhs.ty.zigTypeTag(); | |
| 1010 | ||
| 1011 | if (lhs_ty_tag == .Vector and rhs_ty_tag == .Vector) { | |
| 1012 | if (lhs.ty.arrayLen() != rhs.ty.arrayLen()) { | |
| 1013 | return self.fail(src, "vector length mismatch: {} and {}", .{ | |
| 1014 | lhs.ty.arrayLen(), | |
| 1015 | rhs.ty.arrayLen(), | |
| 1016 | }); | |
| 1017 | } | |
| 1018 | return self.fail(src, "TODO implement support for vectors in cmpNumeric", .{}); | |
| 1019 | } else if (lhs_ty_tag == .Vector or rhs_ty_tag == .Vector) { | |
| 1020 | return self.fail(src, "mixed scalar and vector operands to comparison operator: '{}' and '{}'", .{ | |
| 1021 | lhs.ty, | |
| 1022 | rhs.ty, | |
| 1023 | }); | |
| 1024 | } | |
| 1025 | ||
| 1026 | if (lhs.value()) |lhs_val| { | |
| 1027 | if (rhs.value()) |rhs_val| { | |
| 1028 | return self.constBool(src, Value.compare(lhs_val, op, rhs_val)); | |
| 1029 | } | |
| 1030 | } | |
| 1031 | ||
| 1032 | // TODO handle comparisons against lazy zero values | |
| 1033 | // Some values can be compared against zero without being runtime known or without forcing | |
| 1034 | // a full resolution of their value, for example `@sizeOf(@Frame(function))` is known to | |
| 1035 | // always be nonzero, and we benefit from not forcing the full evaluation and stack frame layout | |
| 1036 | // of this function if we don't need to. | |
| 1037 | ||
| 1038 | // It must be a runtime comparison. | |
| 1039 | const b = try self.requireRuntimeBlock(block, src); | |
| 1040 | // For floats, emit a float comparison instruction. | |
| 1041 | const lhs_is_float = switch (lhs_ty_tag) { | |
| 1042 | .Float, .ComptimeFloat => true, | |
| 1043 | else => false, | |
| 1044 | }; | |
| 1045 | const rhs_is_float = switch (rhs_ty_tag) { | |
| 1046 | .Float, .ComptimeFloat => true, | |
| 1047 | else => false, | |
| 1048 | }; | |
| 1049 | if (lhs_is_float and rhs_is_float) { | |
| 1050 | // Implicit cast the smaller one to the larger one. | |
| 1051 | const dest_type = x: { | |
| 1052 | if (lhs_ty_tag == .ComptimeFloat) { | |
| 1053 | break :x rhs.ty; | |
| 1054 | } else if (rhs_ty_tag == .ComptimeFloat) { | |
| 1055 | break :x lhs.ty; | |
| 1056 | } | |
| 1057 | if (lhs.ty.floatBits(self.target) >= rhs.ty.floatBits(self.target)) { | |
| 1058 | break :x lhs.ty; | |
| 1059 | } else { | |
| 1060 | break :x rhs.ty; | |
| 1061 | } | |
| 1062 | }; | |
| 1063 | const casted_lhs = try self.coerce(block, dest_type, lhs); | |
| 1064 | const casted_rhs = try self.coerce(block, dest_type, rhs); | |
| 1065 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){ | |
| 1066 | .lhs = casted_lhs, | |
| 1067 | .rhs = casted_rhs, | |
| 1068 | .op = op, | |
| 1069 | }); | |
| 1070 | } | |
| 1071 | // For mixed unsigned integer sizes, implicit cast both operands to the larger integer. | |
| 1072 | // For mixed signed and unsigned integers, implicit cast both operands to a signed | |
| 1073 | // integer with + 1 bit. | |
| 1074 | // For mixed floats and integers, extract the integer part from the float, cast that to | |
| 1075 | // a signed integer with mantissa bits + 1, and if there was any non-integral part of the float, | |
| 1076 | // add/subtract 1. | |
| 1077 | const lhs_is_signed = if (lhs.value()) |lhs_val| | |
| 1078 | lhs_val.compareWithZero(.lt) | |
| 1079 | else | |
| 1080 | (lhs.ty.isFloat() or lhs.ty.isSignedInt()); | |
| 1081 | const rhs_is_signed = if (rhs.value()) |rhs_val| | |
| 1082 | rhs_val.compareWithZero(.lt) | |
| 1083 | else | |
| 1084 | (rhs.ty.isFloat() or rhs.ty.isSignedInt()); | |
| 1085 | const dest_int_is_signed = lhs_is_signed or rhs_is_signed; | |
| 1086 | ||
| 1087 | var dest_float_type: ?Type = null; | |
| 1088 | ||
| 1089 | var lhs_bits: usize = undefined; | |
| 1090 | if (lhs.value()) |lhs_val| { | |
| 1091 | if (lhs_val.isUndef()) | |
| 1092 | return self.constUndef(src, Type.initTag(.bool)); | |
| 1093 | const is_unsigned = if (lhs_is_float) x: { | |
| 1094 | var bigint_space: Value.BigIntSpace = undefined; | |
| 1095 | var bigint = try lhs_val.toBigInt(&bigint_space).toManaged(self.allocator); | |
| 1096 | defer bigint.deinit(); | |
| 1097 | const zcmp = lhs_val.orderAgainstZero(); | |
| 1098 | if (lhs_val.floatHasFraction()) { | |
| 1099 | switch (op) { | |
| 1100 | .eq => return self.constBool(src, false), | |
| 1101 | .neq => return self.constBool(src, true), | |
| 1102 | else => {}, | |
| 1103 | } | |
| 1104 | if (zcmp == .lt) { | |
| 1105 | try bigint.addScalar(bigint.toConst(), -1); | |
| 1106 | } else { | |
| 1107 | try bigint.addScalar(bigint.toConst(), 1); | |
| 1108 | } | |
| 1109 | } | |
| 1110 | lhs_bits = bigint.toConst().bitCountTwosComp(); | |
| 1111 | break :x (zcmp != .lt); | |
| 1112 | } else x: { | |
| 1113 | lhs_bits = lhs_val.intBitCountTwosComp(); | |
| 1114 | break :x (lhs_val.orderAgainstZero() != .lt); | |
| 1115 | }; | |
| 1116 | lhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | |
| 1117 | } else if (lhs_is_float) { | |
| 1118 | dest_float_type = lhs.ty; | |
| 1119 | } else { | |
| 1120 | const int_info = lhs.ty.intInfo(self.target); | |
| 1121 | lhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); | |
| 1122 | } | |
| 1123 | ||
| 1124 | var rhs_bits: usize = undefined; | |
| 1125 | if (rhs.value()) |rhs_val| { | |
| 1126 | if (rhs_val.isUndef()) | |
| 1127 | return self.constUndef(src, Type.initTag(.bool)); | |
| 1128 | const is_unsigned = if (rhs_is_float) x: { | |
| 1129 | var bigint_space: Value.BigIntSpace = undefined; | |
| 1130 | var bigint = try rhs_val.toBigInt(&bigint_space).toManaged(self.allocator); | |
| 1131 | defer bigint.deinit(); | |
| 1132 | const zcmp = rhs_val.orderAgainstZero(); | |
| 1133 | if (rhs_val.floatHasFraction()) { | |
| 1134 | switch (op) { | |
| 1135 | .eq => return self.constBool(src, false), | |
| 1136 | .neq => return self.constBool(src, true), | |
| 1137 | else => {}, | |
| 1138 | } | |
| 1139 | if (zcmp == .lt) { | |
| 1140 | try bigint.addScalar(bigint.toConst(), -1); | |
| 1141 | } else { | |
| 1142 | try bigint.addScalar(bigint.toConst(), 1); | |
| 1143 | } | |
| 1144 | } | |
| 1145 | rhs_bits = bigint.toConst().bitCountTwosComp(); | |
| 1146 | break :x (zcmp != .lt); | |
| 1147 | } else x: { | |
| 1148 | rhs_bits = rhs_val.intBitCountTwosComp(); | |
| 1149 | break :x (rhs_val.orderAgainstZero() != .lt); | |
| 1150 | }; | |
| 1151 | rhs_bits += @boolToInt(is_unsigned and dest_int_is_signed); | |
| 1152 | } else if (rhs_is_float) { | |
| 1153 | dest_float_type = rhs.ty; | |
| 1154 | } else { | |
| 1155 | const int_info = rhs.ty.intInfo(self.target); | |
| 1156 | rhs_bits = int_info.bits + @boolToInt(!int_info.signed and dest_int_is_signed); | |
| 1157 | } | |
| 1158 | ||
| 1159 | const dest_type = if (dest_float_type) |ft| ft else blk: { | |
| 1160 | const max_bits = std.math.max(lhs_bits, rhs_bits); | |
| 1161 | const casted_bits = std.math.cast(u16, max_bits) catch |err| switch (err) { | |
| 1162 | error.Overflow => return self.fail(src, "{} exceeds maximum integer bit count", .{max_bits}), | |
| 1163 | }; | |
| 1164 | break :blk try self.makeIntType(dest_int_is_signed, casted_bits); | |
| 1165 | }; | |
| 1166 | const casted_lhs = try self.coerce(block, dest_type, lhs); | |
| 1167 | const casted_rhs = try self.coerce(block, dest_type, lhs); | |
| 1168 | ||
| 1169 | return self.addNewInstArgs(b, src, dest_type, Inst.Cmp, Inst.Args(Inst.Cmp){ | |
| 1170 | .lhs = casted_lhs, | |
| 1171 | .rhs = casted_rhs, | |
| 1172 | .op = op, | |
| 1173 | }); | |
| 1174 | } | |
| 1175 | ||
| 1176 | fn makeIntType(self: *Analyze, signed: bool, bits: u16) !Type { | |
| 1177 | if (signed) { | |
| 1178 | const int_payload = try self.arena.allocator.create(Type.Payload.IntSigned); | |
| 1179 | int_payload.* = .{ .bits = bits }; | |
| 1180 | return Type.initPayload(&int_payload.base); | |
| 1181 | } else { | |
| 1182 | const int_payload = try self.arena.allocator.create(Type.Payload.IntUnsigned); | |
| 1183 | int_payload.* = .{ .bits = bits }; | |
| 1184 | return Type.initPayload(&int_payload.base); | |
| 1185 | } | |
| 685 | 1186 | } |
| 686 | 1187 | |
| 687 | fn coerce(self: *Analyze, func: ?*Fn, dest_type: Type, inst: *Inst) !*Inst { | |
| 1188 | fn coerce(self: *Analyze, block: ?*Block, dest_type: Type, inst: *Inst) !*Inst { | |
| 688 | 1189 | // If the types are the same, we can return the operand. |
| 689 | 1190 | if (dest_type.eql(inst.ty)) |
| 690 | 1191 | return inst; |
| 691 | 1192 | |
| 692 | 1193 | const in_memory_result = coerceInMemoryAllowed(dest_type, inst.ty); |
| 693 | 1194 | if (in_memory_result == .ok) { |
| 694 | return self.bitcast(func, dest_type, inst); | |
| 1195 | return self.bitcast(block, dest_type, inst); | |
| 695 | 1196 | } |
| 696 | 1197 | |
| 697 | 1198 | // *[N]T to []T |
| ... | ... | @@ -735,14 +1236,14 @@ const Analyze = struct { |
| 735 | 1236 | return self.fail(inst.src, "TODO implement type coercion from {} to {}", .{ inst.ty, dest_type }); |
| 736 | 1237 | } |
| 737 | 1238 | |
| 738 | fn bitcast(self: *Analyze, func: ?*Fn, dest_type: Type, inst: *Inst) !*Inst { | |
| 1239 | fn bitcast(self: *Analyze, block: ?*Block, dest_type: Type, inst: *Inst) !*Inst { | |
| 739 | 1240 | if (inst.value()) |val| { |
| 740 | 1241 | // Keep the comptime Value representation; take the new type. |
| 741 | 1242 | return self.constInst(inst.src, .{ .ty = dest_type, .val = val }); |
| 742 | 1243 | } |
| 743 | 1244 | // TODO validate the type size and other compile errors |
| 744 | const f = try self.requireFunctionBody(func, inst.src); | |
| 745 | return self.addNewInstArgs(f, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst }); | |
| 1245 | const b = try self.requireRuntimeBlock(block, inst.src); | |
| 1246 | return self.addNewInstArgs(b, inst.src, dest_type, Inst.BitCast, Inst.Args(Inst.BitCast){ .operand = inst }); | |
| 746 | 1247 | } |
| 747 | 1248 | |
| 748 | 1249 | fn coerceArrayPtrToSlice(self: *Analyze, dest_type: Type, inst: *Inst) !*Inst { |
| ... | ... | @@ -784,18 +1285,20 @@ pub fn main() anyerror!void { |
| 784 | 1285 | const allocator = if (std.builtin.link_libc) std.heap.c_allocator else &arena.allocator; |
| 785 | 1286 | |
| 786 | 1287 | const args = try std.process.argsAlloc(allocator); |
| 1288 | defer std.process.argsFree(allocator, args); | |
| 787 | 1289 | |
| 788 | 1290 | const src_path = args[1]; |
| 789 | 1291 | const debug_error_trace = true; |
| 790 | 1292 | |
| 791 | 1293 | const source = try std.fs.cwd().readFileAllocOptions(allocator, src_path, std.math.maxInt(u32), 1, 0); |
| 1294 | defer allocator.free(source); | |
| 792 | 1295 | |
| 793 | 1296 | var zir_module = try text.parse(allocator, source); |
| 794 | 1297 | defer zir_module.deinit(allocator); |
| 795 | 1298 | |
| 796 | 1299 | if (zir_module.errors.len != 0) { |
| 797 | 1300 | for (zir_module.errors) |err_msg| { |
| 798 | const loc = findLineColumn(source, err_msg.byte_offset); | |
| 1301 | const loc = std.zig.findLineColumn(source, err_msg.byte_offset); | |
| 799 | 1302 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); |
| 800 | 1303 | } |
| 801 | 1304 | if (debug_error_trace) return error.ParseFailure; |
| ... | ... | @@ -804,15 +1307,20 @@ pub fn main() anyerror!void { |
| 804 | 1307 | |
| 805 | 1308 | const native_info = try std.zig.system.NativeTargetInfo.detect(allocator, .{}); |
| 806 | 1309 | |
| 807 | var analyzed_module = try analyze(allocator, zir_module, native_info.target); | |
| 1310 | var analyzed_module = try analyze(allocator, zir_module, .{ | |
| 1311 | .target = native_info.target, | |
| 1312 | .output_mode = .Obj, | |
| 1313 | .link_mode = .Static, | |
| 1314 | .optimize_mode = .Debug, | |
| 1315 | }); | |
| 808 | 1316 | defer analyzed_module.deinit(allocator); |
| 809 | 1317 | |
| 810 | 1318 | if (analyzed_module.errors.len != 0) { |
| 811 | 1319 | for (analyzed_module.errors) |err_msg| { |
| 812 | const loc = findLineColumn(source, err_msg.byte_offset); | |
| 1320 | const loc = std.zig.findLineColumn(source, err_msg.byte_offset); | |
| 813 | 1321 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); |
| 814 | 1322 | } |
| 815 | if (debug_error_trace) return error.ParseFailure; | |
| 1323 | if (debug_error_trace) return error.AnalysisFail; | |
| 816 | 1324 | std.process.exit(1); |
| 817 | 1325 | } |
| 818 | 1326 | |
| ... | ... | @@ -827,34 +1335,17 @@ pub fn main() anyerror!void { |
| 827 | 1335 | } |
| 828 | 1336 | |
| 829 | 1337 | const link = @import("link.zig"); |
| 830 | var result = try link.updateExecutableFilePath(allocator, analyzed_module, std.fs.cwd(), "a.out"); | |
| 1338 | var result = try link.updateFilePath(allocator, analyzed_module, std.fs.cwd(), "zir.o"); | |
| 831 | 1339 | defer result.deinit(allocator); |
| 832 | 1340 | if (result.errors.len != 0) { |
| 833 | 1341 | for (result.errors) |err_msg| { |
| 834 | const loc = findLineColumn(source, err_msg.byte_offset); | |
| 1342 | const loc = std.zig.findLineColumn(source, err_msg.byte_offset); | |
| 835 | 1343 | std.debug.warn("{}:{}:{}: error: {}\n", .{ src_path, loc.line + 1, loc.column + 1, err_msg.msg }); |
| 836 | 1344 | } |
| 837 | if (debug_error_trace) return error.ParseFailure; | |
| 1345 | if (debug_error_trace) return error.LinkFailure; | |
| 838 | 1346 | std.process.exit(1); |
| 839 | 1347 | } |
| 840 | 1348 | } |
| 841 | 1349 | |
| 842 | fn findLineColumn(source: []const u8, byte_offset: usize) struct { line: usize, column: usize } { | |
| 843 | var line: usize = 0; | |
| 844 | var column: usize = 0; | |
| 845 | for (source[0..byte_offset]) |byte| { | |
| 846 | switch (byte) { | |
| 847 | '\n' => { | |
| 848 | line += 1; | |
| 849 | column = 0; | |
| 850 | }, | |
| 851 | else => { | |
| 852 | column += 1; | |
| 853 | }, | |
| 854 | } | |
| 855 | } | |
| 856 | return .{ .line = line, .column = column }; | |
| 857 | } | |
| 858 | ||
| 859 | 1350 | // Performance optimization ideas: |
| 860 | 1351 | // * when analyzing use a field in the Inst instead of HashMap to track corresponding instructions |
src-self-hosted/ir/text.zig+292-130| ... | ... | @@ -4,7 +4,8 @@ const std = @import("std"); |
| 4 | 4 | const mem = std.mem; |
| 5 | 5 | const Allocator = std.mem.Allocator; |
| 6 | 6 | const assert = std.debug.assert; |
| 7 | const BigInt = std.math.big.Int; | |
| 7 | const BigIntConst = std.math.big.int.Const; | |
| 8 | const BigIntMutable = std.math.big.int.Mutable; | |
| 8 | 9 | const Type = @import("../type.zig").Type; |
| 9 | 10 | const Value = @import("../value.zig").Value; |
| 10 | 11 | const ir = @import("../ir.zig"); |
| ... | ... | @@ -18,6 +19,7 @@ pub const Inst = struct { |
| 18 | 19 | |
| 19 | 20 | /// These names are used directly as the instruction names in the text format. |
| 20 | 21 | pub const Tag = enum { |
| 22 | breakpoint, | |
| 21 | 23 | str, |
| 22 | 24 | int, |
| 23 | 25 | ptrtoint, |
| ... | ... | @@ -26,6 +28,7 @@ pub const Inst = struct { |
| 26 | 28 | as, |
| 27 | 29 | @"asm", |
| 28 | 30 | @"unreachable", |
| 31 | @"return", | |
| 29 | 32 | @"fn", |
| 30 | 33 | @"export", |
| 31 | 34 | primitive, |
| ... | ... | @@ -34,10 +37,15 @@ pub const Inst = struct { |
| 34 | 37 | bitcast, |
| 35 | 38 | elemptr, |
| 36 | 39 | add, |
| 40 | cmp, | |
| 41 | condbr, | |
| 42 | isnull, | |
| 43 | isnonnull, | |
| 37 | 44 | }; |
| 38 | 45 | |
| 39 | 46 | pub fn TagToType(tag: Tag) type { |
| 40 | 47 | return switch (tag) { |
| 48 | .breakpoint => Breakpoint, | |
| 41 | 49 | .str => Str, |
| 42 | 50 | .int => Int, |
| 43 | 51 | .ptrtoint => PtrToInt, |
| ... | ... | @@ -46,6 +54,7 @@ pub const Inst = struct { |
| 46 | 54 | .as => As, |
| 47 | 55 | .@"asm" => Asm, |
| 48 | 56 | .@"unreachable" => Unreachable, |
| 57 | .@"return" => Return, | |
| 49 | 58 | .@"fn" => Fn, |
| 50 | 59 | .@"export" => Export, |
| 51 | 60 | .primitive => Primitive, |
| ... | ... | @@ -54,6 +63,10 @@ pub const Inst = struct { |
| 54 | 63 | .bitcast => BitCast, |
| 55 | 64 | .elemptr => ElemPtr, |
| 56 | 65 | .add => Add, |
| 66 | .cmp => Cmp, | |
| 67 | .condbr => CondBr, | |
| 68 | .isnull => IsNull, | |
| 69 | .isnonnull => IsNonNull, | |
| 57 | 70 | }; |
| 58 | 71 | } |
| 59 | 72 | |
| ... | ... | @@ -64,6 +77,14 @@ pub const Inst = struct { |
| 64 | 77 | return @fieldParentPtr(T, "base", base); |
| 65 | 78 | } |
| 66 | 79 | |
| 80 | pub const Breakpoint = struct { | |
| 81 | pub const base_tag = Tag.breakpoint; | |
| 82 | base: Inst, | |
| 83 | ||
| 84 | positionals: struct {}, | |
| 85 | kw_args: struct {}, | |
| 86 | }; | |
| 87 | ||
| 67 | 88 | pub const Str = struct { |
| 68 | 89 | pub const base_tag = Tag.str; |
| 69 | 90 | base: Inst, |
| ... | ... | @@ -79,7 +100,7 @@ pub const Inst = struct { |
| 79 | 100 | base: Inst, |
| 80 | 101 | |
| 81 | 102 | positionals: struct { |
| 82 | int: BigInt, | |
| 103 | int: BigIntConst, | |
| 83 | 104 | }, |
| 84 | 105 | kw_args: struct {}, |
| 85 | 106 | }; |
| ... | ... | @@ -151,19 +172,23 @@ pub const Inst = struct { |
| 151 | 172 | kw_args: struct {}, |
| 152 | 173 | }; |
| 153 | 174 | |
| 175 | pub const Return = struct { | |
| 176 | pub const base_tag = Tag.@"return"; | |
| 177 | base: Inst, | |
| 178 | ||
| 179 | positionals: struct {}, | |
| 180 | kw_args: struct {}, | |
| 181 | }; | |
| 182 | ||
| 154 | 183 | pub const Fn = struct { |
| 155 | 184 | pub const base_tag = Tag.@"fn"; |
| 156 | 185 | base: Inst, |
| 157 | 186 | |
| 158 | 187 | positionals: struct { |
| 159 | 188 | fn_type: *Inst, |
| 160 | body: Body, | |
| 189 | body: Module.Body, | |
| 161 | 190 | }, |
| 162 | 191 | kw_args: struct {}, |
| 163 | ||
| 164 | pub const Body = struct { | |
| 165 | instructions: []*Inst, | |
| 166 | }; | |
| 167 | 192 | }; |
| 168 | 193 | |
| 169 | 194 | pub const Export = struct { |
| ... | ... | @@ -297,6 +322,50 @@ pub const Inst = struct { |
| 297 | 322 | }, |
| 298 | 323 | kw_args: struct {}, |
| 299 | 324 | }; |
| 325 | ||
| 326 | pub const Cmp = struct { | |
| 327 | pub const base_tag = Tag.cmp; | |
| 328 | base: Inst, | |
| 329 | ||
| 330 | positionals: struct { | |
| 331 | lhs: *Inst, | |
| 332 | op: std.math.CompareOperator, | |
| 333 | rhs: *Inst, | |
| 334 | }, | |
| 335 | kw_args: struct {}, | |
| 336 | }; | |
| 337 | ||
| 338 | pub const CondBr = struct { | |
| 339 | pub const base_tag = Tag.condbr; | |
| 340 | base: Inst, | |
| 341 | ||
| 342 | positionals: struct { | |
| 343 | condition: *Inst, | |
| 344 | true_body: Module.Body, | |
| 345 | false_body: Module.Body, | |
| 346 | }, | |
| 347 | kw_args: struct {}, | |
| 348 | }; | |
| 349 | ||
| 350 | pub const IsNull = struct { | |
| 351 | pub const base_tag = Tag.isnull; | |
| 352 | base: Inst, | |
| 353 | ||
| 354 | positionals: struct { | |
| 355 | operand: *Inst, | |
| 356 | }, | |
| 357 | kw_args: struct {}, | |
| 358 | }; | |
| 359 | ||
| 360 | pub const IsNonNull = struct { | |
| 361 | pub const base_tag = Tag.isnonnull; | |
| 362 | base: Inst, | |
| 363 | ||
| 364 | positionals: struct { | |
| 365 | operand: *Inst, | |
| 366 | }, | |
| 367 | kw_args: struct {}, | |
| 368 | }; | |
| 300 | 369 | }; |
| 301 | 370 | |
| 302 | 371 | pub const ErrorMsg = struct { |
| ... | ... | @@ -309,6 +378,10 @@ pub const Module = struct { |
| 309 | 378 | errors: []ErrorMsg, |
| 310 | 379 | arena: std.heap.ArenaAllocator, |
| 311 | 380 | |
| 381 | pub const Body = struct { | |
| 382 | instructions: []*Inst, | |
| 383 | }; | |
| 384 | ||
| 312 | 385 | pub fn deinit(self: *Module, allocator: *Allocator) void { |
| 313 | 386 | allocator.free(self.decls); |
| 314 | 387 | allocator.free(self.errors); |
| ... | ... | @@ -321,7 +394,7 @@ pub const Module = struct { |
| 321 | 394 | self.writeToStream(std.heap.page_allocator, std.io.getStdErr().outStream()) catch {}; |
| 322 | 395 | } |
| 323 | 396 | |
| 324 | const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Inst.Fn.Body }); | |
| 397 | const InstPtrTable = std.AutoHashMap(*Inst, struct { index: usize, fn_body: ?*Module.Body }); | |
| 325 | 398 | |
| 326 | 399 | /// The allocator is used for temporary storage, but this function always returns |
| 327 | 400 | /// with no resources allocated. |
| ... | ... | @@ -357,6 +430,7 @@ pub const Module = struct { |
| 357 | 430 | ) @TypeOf(stream).Error!void { |
| 358 | 431 | // TODO I tried implementing this with an inline for loop and hit a compiler bug |
| 359 | 432 | switch (decl.tag) { |
| 433 | .breakpoint => return self.writeInstToStreamGeneric(stream, .breakpoint, decl, inst_table), | |
| 360 | 434 | .str => return self.writeInstToStreamGeneric(stream, .str, decl, inst_table), |
| 361 | 435 | .int => return self.writeInstToStreamGeneric(stream, .int, decl, inst_table), |
| 362 | 436 | .ptrtoint => return self.writeInstToStreamGeneric(stream, .ptrtoint, decl, inst_table), |
| ... | ... | @@ -365,6 +439,7 @@ pub const Module = struct { |
| 365 | 439 | .as => return self.writeInstToStreamGeneric(stream, .as, decl, inst_table), |
| 366 | 440 | .@"asm" => return self.writeInstToStreamGeneric(stream, .@"asm", decl, inst_table), |
| 367 | 441 | .@"unreachable" => return self.writeInstToStreamGeneric(stream, .@"unreachable", decl, inst_table), |
| 442 | .@"return" => return self.writeInstToStreamGeneric(stream, .@"return", decl, inst_table), | |
| 368 | 443 | .@"fn" => return self.writeInstToStreamGeneric(stream, .@"fn", decl, inst_table), |
| 369 | 444 | .@"export" => return self.writeInstToStreamGeneric(stream, .@"export", decl, inst_table), |
| 370 | 445 | .primitive => return self.writeInstToStreamGeneric(stream, .primitive, decl, inst_table), |
| ... | ... | @@ -373,6 +448,10 @@ pub const Module = struct { |
| 373 | 448 | .bitcast => return self.writeInstToStreamGeneric(stream, .bitcast, decl, inst_table), |
| 374 | 449 | .elemptr => return self.writeInstToStreamGeneric(stream, .elemptr, decl, inst_table), |
| 375 | 450 | .add => return self.writeInstToStreamGeneric(stream, .add, decl, inst_table), |
| 451 | .cmp => return self.writeInstToStreamGeneric(stream, .cmp, decl, inst_table), | |
| 452 | .condbr => return self.writeInstToStreamGeneric(stream, .condbr, decl, inst_table), | |
| 453 | .isnull => return self.writeInstToStreamGeneric(stream, .isnull, decl, inst_table), | |
| 454 | .isnonnull => return self.writeInstToStreamGeneric(stream, .isnonnull, decl, inst_table), | |
| 376 | 455 | } |
| 377 | 456 | } |
| 378 | 457 | |
| ... | ... | @@ -432,7 +511,7 @@ pub const Module = struct { |
| 432 | 511 | } |
| 433 | 512 | try stream.writeByte(']'); |
| 434 | 513 | }, |
| 435 | Inst.Fn.Body => { | |
| 514 | Module.Body => { | |
| 436 | 515 | try stream.writeAll("{\n"); |
| 437 | 516 | for (param.instructions) |inst, i| { |
| 438 | 517 | try stream.print(" %{} ", .{i}); |
| ... | ... | @@ -443,7 +522,7 @@ pub const Module = struct { |
| 443 | 522 | }, |
| 444 | 523 | bool => return stream.writeByte("01"[@boolToInt(param)]), |
| 445 | 524 | []u8, []const u8 => return std.zig.renderStringLiteral(param, stream), |
| 446 | BigInt => return stream.print("{}", .{param}), | |
| 525 | BigIntConst => return stream.print("{}", .{param}), | |
| 447 | 526 | else => |T| @compileError("unimplemented: rendering parameter of type " ++ @typeName(T)), |
| 448 | 527 | } |
| 449 | 528 | } |
| ... | ... | @@ -497,7 +576,7 @@ const Parser = struct { |
| 497 | 576 | name_map: std.StringHashMap(usize), |
| 498 | 577 | }; |
| 499 | 578 | |
| 500 | fn parseBody(self: *Parser) !Inst.Fn.Body { | |
| 579 | fn parseBody(self: *Parser) !Module.Body { | |
| 501 | 580 | var body_context = Body{ |
| 502 | 581 | .instructions = std.ArrayList(*Inst).init(self.allocator), |
| 503 | 582 | .name_map = std.StringHashMap(usize).init(self.allocator), |
| ... | ... | @@ -532,9 +611,10 @@ const Parser = struct { |
| 532 | 611 | else => |byte| return self.failByte(byte), |
| 533 | 612 | }; |
| 534 | 613 | |
| 535 | return Inst.Fn.Body{ | |
| 536 | .instructions = body_context.instructions.toOwnedSlice(), | |
| 537 | }; | |
| 614 | // Move the instructions to the arena | |
| 615 | const instrs = try self.arena.allocator.alloc(*Inst, body_context.instructions.items.len); | |
| 616 | mem.copy(*Inst, instrs, body_context.instructions.items); | |
| 617 | return Module.Body{ .instructions = instrs }; | |
| 538 | 618 | } |
| 539 | 619 | |
| 540 | 620 | fn parseStringLiteral(self: *Parser) ![]u8 { |
| ... | ... | @@ -565,7 +645,7 @@ const Parser = struct { |
| 565 | 645 | }; |
| 566 | 646 | } |
| 567 | 647 | |
| 568 | fn parseIntegerLiteral(self: *Parser) !BigInt { | |
| 648 | fn parseIntegerLiteral(self: *Parser) !BigIntConst { | |
| 569 | 649 | const start = self.i; |
| 570 | 650 | if (self.source[self.i] == '-') self.i += 1; |
| 571 | 651 | while (true) : (self.i += 1) switch (self.source[self.i]) { |
| ... | ... | @@ -573,41 +653,46 @@ const Parser = struct { |
| 573 | 653 | else => break, |
| 574 | 654 | }; |
| 575 | 655 | const number_text = self.source[start..self.i]; |
| 576 | var result = try BigInt.init(&self.arena.allocator); | |
| 577 | result.setString(10, number_text) catch |err| { | |
| 578 | self.i = start; | |
| 579 | switch (err) { | |
| 580 | error.InvalidBase => unreachable, | |
| 581 | error.InvalidCharForDigit => return self.fail("invalid digit in integer literal", .{}), | |
| 582 | error.DigitTooLargeForBase => return self.fail("digit too large in integer literal", .{}), | |
| 583 | else => |e| return e, | |
| 584 | } | |
| 656 | const base = 10; | |
| 657 | // TODO reuse the same array list for this | |
| 658 | const limbs_buffer_len = std.math.big.int.calcSetStringLimbsBufferLen(base, number_text.len); | |
| 659 | const limbs_buffer = try self.allocator.alloc(std.math.big.Limb, limbs_buffer_len); | |
| 660 | defer self.allocator.free(limbs_buffer); | |
| 661 | const limb_len = std.math.big.int.calcSetStringLimbCount(base, number_text.len); | |
| 662 | const limbs = try self.arena.allocator.alloc(std.math.big.Limb, limb_len); | |
| 663 | var result = BigIntMutable{ .limbs = limbs, .positive = undefined, .len = undefined }; | |
| 664 | result.setString(base, number_text, limbs_buffer, self.allocator) catch |err| switch (err) { | |
| 665 | error.InvalidCharacter => { | |
| 666 | self.i = start; | |
| 667 | return self.fail("invalid digit in integer literal", .{}); | |
| 668 | }, | |
| 585 | 669 | }; |
| 586 | return result; | |
| 670 | return result.toConst(); | |
| 587 | 671 | } |
| 588 | 672 | |
| 589 | 673 | fn parseRoot(self: *Parser) !void { |
| 590 | 674 | // The IR format is designed so that it can be tokenized and parsed at the same time. |
| 591 | while (true) : (self.i += 1) switch (self.source[self.i]) { | |
| 592 | ';' => _ = try skipToAndOver(self, '\n'), | |
| 593 | '@' => { | |
| 594 | self.i += 1; | |
| 595 | const ident = try skipToAndOver(self, ' '); | |
| 596 | skipSpace(self); | |
| 597 | try requireEatBytes(self, "="); | |
| 598 | skipSpace(self); | |
| 599 | const inst = try parseInstruction(self, null); | |
| 600 | const ident_index = self.decls.items.len; | |
| 601 | if (try self.global_name_map.put(ident, ident_index)) |_| { | |
| 602 | return self.fail("redefinition of identifier '{}'", .{ident}); | |
| 603 | } | |
| 604 | try self.decls.append(inst); | |
| 605 | continue; | |
| 606 | }, | |
| 607 | ' ', '\n' => continue, | |
| 608 | 0 => break, | |
| 609 | else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}), | |
| 610 | }; | |
| 675 | while (true) { | |
| 676 | switch (self.source[self.i]) { | |
| 677 | ';' => _ = try skipToAndOver(self, '\n'), | |
| 678 | '@' => { | |
| 679 | self.i += 1; | |
| 680 | const ident = try skipToAndOver(self, ' '); | |
| 681 | skipSpace(self); | |
| 682 | try requireEatBytes(self, "="); | |
| 683 | skipSpace(self); | |
| 684 | const inst = try parseInstruction(self, null); | |
| 685 | const ident_index = self.decls.items.len; | |
| 686 | if (try self.global_name_map.put(ident, ident_index)) |_| { | |
| 687 | return self.fail("redefinition of identifier '{}'", .{ident}); | |
| 688 | } | |
| 689 | try self.decls.append(inst); | |
| 690 | }, | |
| 691 | ' ', '\n' => self.i += 1, | |
| 692 | 0 => break, | |
| 693 | else => |byte| return self.fail("unexpected byte: '{c}'", .{byte}), | |
| 694 | } | |
| 695 | } | |
| 611 | 696 | } |
| 612 | 697 | |
| 613 | 698 | fn eatByte(self: *Parser, byte: u8) bool { |
| ... | ... | @@ -752,7 +837,7 @@ const Parser = struct { |
| 752 | 837 | }; |
| 753 | 838 | } |
| 754 | 839 | switch (T) { |
| 755 | Inst.Fn.Body => return parseBody(self), | |
| 840 | Module.Body => return parseBody(self), | |
| 756 | 841 | bool => { |
| 757 | 842 | const bool_value = switch (self.source[self.i]) { |
| 758 | 843 | '0' => false, |
| ... | ... | @@ -779,7 +864,7 @@ const Parser = struct { |
| 779 | 864 | }, |
| 780 | 865 | *Inst => return parseParameterInst(self, body_ctx), |
| 781 | 866 | []u8, []const u8 => return self.parseStringLiteral(), |
| 782 | BigInt => return self.parseIntegerLiteral(), | |
| 867 | BigIntConst => return self.parseIntegerLiteral(), | |
| 783 | 868 | else => @compileError("Unimplemented: ir parseParameterGeneric for type " ++ @typeName(T)), |
| 784 | 869 | } |
| 785 | 870 | return self.fail("TODO parse parameter {}", .{@typeName(T)}); |
| ... | ... | @@ -878,11 +963,12 @@ const EmitZIR = struct { |
| 878 | 963 | } |
| 879 | 964 | |
| 880 | 965 | fn emitComptimeIntVal(self: *EmitZIR, src: usize, val: Value) !*Inst { |
| 966 | const big_int_space = try self.arena.allocator.create(Value.BigIntSpace); | |
| 881 | 967 | const int_inst = try self.arena.allocator.create(Inst.Int); |
| 882 | 968 | int_inst.* = .{ |
| 883 | 969 | .base = .{ .src = src, .tag = Inst.Int.base_tag }, |
| 884 | 970 | .positionals = .{ |
| 885 | .int = try val.toBigInt(&self.arena.allocator), | |
| 971 | .int = val.toBigInt(big_int_space), | |
| 886 | 972 | }, |
| 887 | 973 | .kw_args = .{}, |
| 888 | 974 | }; |
| ... | ... | @@ -937,96 +1023,19 @@ const EmitZIR = struct { |
| 937 | 1023 | var instructions = std.ArrayList(*Inst).init(self.allocator); |
| 938 | 1024 | defer instructions.deinit(); |
| 939 | 1025 | |
| 940 | for (module_fn.body) |inst| { | |
| 941 | const new_inst = switch (inst.tag) { | |
| 942 | .unreach => blk: { | |
| 943 | const unreach_inst = try self.arena.allocator.create(Inst.Unreachable); | |
| 944 | unreach_inst.* = .{ | |
| 945 | .base = .{ .src = inst.src, .tag = Inst.Unreachable.base_tag }, | |
| 946 | .positionals = .{}, | |
| 947 | .kw_args = .{}, | |
| 948 | }; | |
| 949 | break :blk &unreach_inst.base; | |
| 950 | }, | |
| 951 | .constant => unreachable, // excluded from function bodies | |
| 952 | .assembly => blk: { | |
| 953 | const old_inst = inst.cast(ir.Inst.Assembly).?; | |
| 954 | const new_inst = try self.arena.allocator.create(Inst.Asm); | |
| 955 | ||
| 956 | const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len); | |
| 957 | for (inputs) |*elem, i| { | |
| 958 | elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]); | |
| 959 | } | |
| 960 | ||
| 961 | const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len); | |
| 962 | for (clobbers) |*elem, i| { | |
| 963 | elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]); | |
| 964 | } | |
| 965 | ||
| 966 | const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len); | |
| 967 | for (args) |*elem, i| { | |
| 968 | elem.* = try self.resolveInst(&inst_table, old_inst.args.args[i]); | |
| 969 | } | |
| 970 | ||
| 971 | new_inst.* = .{ | |
| 972 | .base = .{ .src = inst.src, .tag = Inst.Asm.base_tag }, | |
| 973 | .positionals = .{ | |
| 974 | .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source), | |
| 975 | .return_type = try self.emitType(inst.src, inst.ty), | |
| 976 | }, | |
| 977 | .kw_args = .{ | |
| 978 | .@"volatile" = old_inst.args.is_volatile, | |
| 979 | .output = if (old_inst.args.output) |o| | |
| 980 | try self.emitStringLiteral(inst.src, o) | |
| 981 | else | |
| 982 | null, | |
| 983 | .inputs = inputs, | |
| 984 | .clobbers = clobbers, | |
| 985 | .args = args, | |
| 986 | }, | |
| 987 | }; | |
| 988 | break :blk &new_inst.base; | |
| 989 | }, | |
| 990 | .ptrtoint => blk: { | |
| 991 | const old_inst = inst.cast(ir.Inst.PtrToInt).?; | |
| 992 | const new_inst = try self.arena.allocator.create(Inst.PtrToInt); | |
| 993 | new_inst.* = .{ | |
| 994 | .base = .{ .src = inst.src, .tag = Inst.PtrToInt.base_tag }, | |
| 995 | .positionals = .{ | |
| 996 | .ptr = try self.resolveInst(&inst_table, old_inst.args.ptr), | |
| 997 | }, | |
| 998 | .kw_args = .{}, | |
| 999 | }; | |
| 1000 | break :blk &new_inst.base; | |
| 1001 | }, | |
| 1002 | .bitcast => blk: { | |
| 1003 | const old_inst = inst.cast(ir.Inst.BitCast).?; | |
| 1004 | const new_inst = try self.arena.allocator.create(Inst.BitCast); | |
| 1005 | new_inst.* = .{ | |
| 1006 | .base = .{ .src = inst.src, .tag = Inst.BitCast.base_tag }, | |
| 1007 | .positionals = .{ | |
| 1008 | .dest_type = try self.emitType(inst.src, inst.ty), | |
| 1009 | .operand = try self.resolveInst(&inst_table, old_inst.args.operand), | |
| 1010 | }, | |
| 1011 | .kw_args = .{}, | |
| 1012 | }; | |
| 1013 | break :blk &new_inst.base; | |
| 1014 | }, | |
| 1015 | }; | |
| 1016 | try instructions.append(new_inst); | |
| 1017 | try inst_table.putNoClobber(inst, new_inst); | |
| 1018 | } | |
| 1026 | try self.emitBody(module_fn.body, &inst_table, &instructions); | |
| 1019 | 1027 | |
| 1020 | 1028 | const fn_type = try self.emitType(src, module_fn.fn_type); |
| 1021 | 1029 | |
| 1030 | const arena_instrs = try self.arena.allocator.alloc(*Inst, instructions.items.len); | |
| 1031 | mem.copy(*Inst, arena_instrs, instructions.items); | |
| 1032 | ||
| 1022 | 1033 | const fn_inst = try self.arena.allocator.create(Inst.Fn); |
| 1023 | 1034 | fn_inst.* = .{ |
| 1024 | 1035 | .base = .{ .src = src, .tag = Inst.Fn.base_tag }, |
| 1025 | 1036 | .positionals = .{ |
| 1026 | 1037 | .fn_type = fn_type, |
| 1027 | .body = .{ | |
| 1028 | .instructions = instructions.toOwnedSlice(), | |
| 1029 | }, | |
| 1038 | .body = .{ .instructions = arena_instrs }, | |
| 1030 | 1039 | }, |
| 1031 | 1040 | .kw_args = .{}, |
| 1032 | 1041 | }; |
| ... | ... | @@ -1037,6 +1046,159 @@ const EmitZIR = struct { |
| 1037 | 1046 | } |
| 1038 | 1047 | } |
| 1039 | 1048 | |
| 1049 | fn emitTrivial(self: *EmitZIR, src: usize, comptime T: type) Allocator.Error!*Inst { | |
| 1050 | const new_inst = try self.arena.allocator.create(T); | |
| 1051 | new_inst.* = .{ | |
| 1052 | .base = .{ .src = src, .tag = T.base_tag }, | |
| 1053 | .positionals = .{}, | |
| 1054 | .kw_args = .{}, | |
| 1055 | }; | |
| 1056 | return &new_inst.base; | |
| 1057 | } | |
| 1058 | ||
| 1059 | fn emitBody( | |
| 1060 | self: *EmitZIR, | |
| 1061 | body: ir.Module.Body, | |
| 1062 | inst_table: *std.AutoHashMap(*ir.Inst, *Inst), | |
| 1063 | instructions: *std.ArrayList(*Inst), | |
| 1064 | ) Allocator.Error!void { | |
| 1065 | for (body.instructions) |inst| { | |
| 1066 | const new_inst = switch (inst.tag) { | |
| 1067 | .breakpoint => try self.emitTrivial(inst.src, Inst.Breakpoint), | |
| 1068 | .unreach => try self.emitTrivial(inst.src, Inst.Unreachable), | |
| 1069 | .ret => try self.emitTrivial(inst.src, Inst.Return), | |
| 1070 | .constant => unreachable, // excluded from function bodies | |
| 1071 | .assembly => blk: { | |
| 1072 | const old_inst = inst.cast(ir.Inst.Assembly).?; | |
| 1073 | const new_inst = try self.arena.allocator.create(Inst.Asm); | |
| 1074 | ||
| 1075 | const inputs = try self.arena.allocator.alloc(*Inst, old_inst.args.inputs.len); | |
| 1076 | for (inputs) |*elem, i| { | |
| 1077 | elem.* = try self.emitStringLiteral(inst.src, old_inst.args.inputs[i]); | |
| 1078 | } | |
| 1079 | ||
| 1080 | const clobbers = try self.arena.allocator.alloc(*Inst, old_inst.args.clobbers.len); | |
| 1081 | for (clobbers) |*elem, i| { | |
| 1082 | elem.* = try self.emitStringLiteral(inst.src, old_inst.args.clobbers[i]); | |
| 1083 | } | |
| 1084 | ||
| 1085 | const args = try self.arena.allocator.alloc(*Inst, old_inst.args.args.len); | |
| 1086 | for (args) |*elem, i| { | |
| 1087 | elem.* = try self.resolveInst(inst_table, old_inst.args.args[i]); | |
| 1088 | } | |
| 1089 | ||
| 1090 | new_inst.* = .{ | |
| 1091 | .base = .{ .src = inst.src, .tag = Inst.Asm.base_tag }, | |
| 1092 | .positionals = .{ | |
| 1093 | .asm_source = try self.emitStringLiteral(inst.src, old_inst.args.asm_source), | |
| 1094 | .return_type = try self.emitType(inst.src, inst.ty), | |
| 1095 | }, | |
| 1096 | .kw_args = .{ | |
| 1097 | .@"volatile" = old_inst.args.is_volatile, | |
| 1098 | .output = if (old_inst.args.output) |o| | |
| 1099 | try self.emitStringLiteral(inst.src, o) | |
| 1100 | else | |
| 1101 | null, | |
| 1102 | .inputs = inputs, | |
| 1103 | .clobbers = clobbers, | |
| 1104 | .args = args, | |
| 1105 | }, | |
| 1106 | }; | |
| 1107 | break :blk &new_inst.base; | |
| 1108 | }, | |
| 1109 | .ptrtoint => blk: { | |
| 1110 | const old_inst = inst.cast(ir.Inst.PtrToInt).?; | |
| 1111 | const new_inst = try self.arena.allocator.create(Inst.PtrToInt); | |
| 1112 | new_inst.* = .{ | |
| 1113 | .base = .{ .src = inst.src, .tag = Inst.PtrToInt.base_tag }, | |
| 1114 | .positionals = .{ | |
| 1115 | .ptr = try self.resolveInst(inst_table, old_inst.args.ptr), | |
| 1116 | }, | |
| 1117 | .kw_args = .{}, | |
| 1118 | }; | |
| 1119 | break :blk &new_inst.base; | |
| 1120 | }, | |
| 1121 | .bitcast => blk: { | |
| 1122 | const old_inst = inst.cast(ir.Inst.BitCast).?; | |
| 1123 | const new_inst = try self.arena.allocator.create(Inst.BitCast); | |
| 1124 | new_inst.* = .{ | |
| 1125 | .base = .{ .src = inst.src, .tag = Inst.BitCast.base_tag }, | |
| 1126 | .positionals = .{ | |
| 1127 | .dest_type = try self.emitType(inst.src, inst.ty), | |
| 1128 | .operand = try self.resolveInst(inst_table, old_inst.args.operand), | |
| 1129 | }, | |
| 1130 | .kw_args = .{}, | |
| 1131 | }; | |
| 1132 | break :blk &new_inst.base; | |
| 1133 | }, | |
| 1134 | .cmp => blk: { | |
| 1135 | const old_inst = inst.cast(ir.Inst.Cmp).?; | |
| 1136 | const new_inst = try self.arena.allocator.create(Inst.Cmp); | |
| 1137 | new_inst.* = .{ | |
| 1138 | .base = .{ .src = inst.src, .tag = Inst.Cmp.base_tag }, | |
| 1139 | .positionals = .{ | |
| 1140 | .lhs = try self.resolveInst(inst_table, old_inst.args.lhs), | |
| 1141 | .rhs = try self.resolveInst(inst_table, old_inst.args.rhs), | |
| 1142 | .op = old_inst.args.op, | |
| 1143 | }, | |
| 1144 | .kw_args = .{}, | |
| 1145 | }; | |
| 1146 | break :blk &new_inst.base; | |
| 1147 | }, | |
| 1148 | .condbr => blk: { | |
| 1149 | const old_inst = inst.cast(ir.Inst.CondBr).?; | |
| 1150 | ||
| 1151 | var true_body = std.ArrayList(*Inst).init(self.allocator); | |
| 1152 | var false_body = std.ArrayList(*Inst).init(self.allocator); | |
| 1153 | ||
| 1154 | defer true_body.deinit(); | |
| 1155 | defer false_body.deinit(); | |
| 1156 | ||
| 1157 | try self.emitBody(old_inst.args.true_body, inst_table, &true_body); | |
| 1158 | try self.emitBody(old_inst.args.false_body, inst_table, &false_body); | |
| 1159 | ||
| 1160 | const new_inst = try self.arena.allocator.create(Inst.CondBr); | |
| 1161 | new_inst.* = .{ | |
| 1162 | .base = .{ .src = inst.src, .tag = Inst.CondBr.base_tag }, | |
| 1163 | .positionals = .{ | |
| 1164 | .condition = try self.resolveInst(inst_table, old_inst.args.condition), | |
| 1165 | .true_body = .{ .instructions = true_body.toOwnedSlice() }, | |
| 1166 | .false_body = .{ .instructions = false_body.toOwnedSlice() }, | |
| 1167 | }, | |
| 1168 | .kw_args = .{}, | |
| 1169 | }; | |
| 1170 | break :blk &new_inst.base; | |
| 1171 | }, | |
| 1172 | .isnull => blk: { | |
| 1173 | const old_inst = inst.cast(ir.Inst.IsNull).?; | |
| 1174 | const new_inst = try self.arena.allocator.create(Inst.IsNull); | |
| 1175 | new_inst.* = .{ | |
| 1176 | .base = .{ .src = inst.src, .tag = Inst.IsNull.base_tag }, | |
| 1177 | .positionals = .{ | |
| 1178 | .operand = try self.resolveInst(inst_table, old_inst.args.operand), | |
| 1179 | }, | |
| 1180 | .kw_args = .{}, | |
| 1181 | }; | |
| 1182 | break :blk &new_inst.base; | |
| 1183 | }, | |
| 1184 | .isnonnull => blk: { | |
| 1185 | const old_inst = inst.cast(ir.Inst.IsNonNull).?; | |
| 1186 | const new_inst = try self.arena.allocator.create(Inst.IsNonNull); | |
| 1187 | new_inst.* = .{ | |
| 1188 | .base = .{ .src = inst.src, .tag = Inst.IsNonNull.base_tag }, | |
| 1189 | .positionals = .{ | |
| 1190 | .operand = try self.resolveInst(inst_table, old_inst.args.operand), | |
| 1191 | }, | |
| 1192 | .kw_args = .{}, | |
| 1193 | }; | |
| 1194 | break :blk &new_inst.base; | |
| 1195 | }, | |
| 1196 | }; | |
| 1197 | try instructions.append(new_inst); | |
| 1198 | try inst_table.putNoClobber(inst, new_inst); | |
| 1199 | } | |
| 1200 | } | |
| 1201 | ||
| 1040 | 1202 | fn emitType(self: *EmitZIR, src: usize, ty: Type) Allocator.Error!*Inst { |
| 1041 | 1203 | switch (ty.tag()) { |
| 1042 | 1204 | .isize => return self.emitPrimitiveType(src, .isize), |
src-self-hosted/link.zig+54-21| ... | ... | @@ -7,11 +7,6 @@ const fs = std.fs; |
| 7 | 7 | const elf = std.elf; |
| 8 | 8 | const codegen = @import("codegen.zig"); |
| 9 | 9 | |
| 10 | /// On common systems with a 0o022 umask, 0o777 will still result in a file created | |
| 11 | /// with 0o755 permissions, but it works appropriately if the system is configured | |
| 12 | /// more leniently. As another data point, C's fopen seems to open files with the | |
| 13 | /// 666 mode. | |
| 14 | const executable_mode = 0o777; | |
| 15 | 10 | const default_entry_addr = 0x8000000; |
| 16 | 11 | |
| 17 | 12 | pub const ErrorMsg = struct { |
| ... | ... | @@ -35,29 +30,29 @@ pub const Result = struct { |
| 35 | 30 | /// If incremental linking fails, falls back to truncating the file and rewriting it. |
| 36 | 31 | /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. |
| 37 | 32 | /// This operation is not atomic. |
| 38 | pub fn updateExecutableFilePath( | |
| 33 | pub fn updateFilePath( | |
| 39 | 34 | allocator: *Allocator, |
| 40 | 35 | module: ir.Module, |
| 41 | 36 | dir: fs.Dir, |
| 42 | 37 | sub_path: []const u8, |
| 43 | 38 | ) !Result { |
| 44 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = executable_mode }); | |
| 39 | const file = try dir.createFile(sub_path, .{ .truncate = false, .read = true, .mode = determineMode(module) }); | |
| 45 | 40 | defer file.close(); |
| 46 | 41 | |
| 47 | return updateExecutableFile(allocator, module, file); | |
| 42 | return updateFile(allocator, module, file); | |
| 48 | 43 | } |
| 49 | 44 | |
| 50 | 45 | /// Atomically overwrites the old file, if present. |
| 51 | pub fn writeExecutableFilePath( | |
| 46 | pub fn writeFilePath( | |
| 52 | 47 | allocator: *Allocator, |
| 53 | 48 | module: ir.Module, |
| 54 | 49 | dir: fs.Dir, |
| 55 | 50 | sub_path: []const u8, |
| 56 | 51 | ) !Result { |
| 57 | const af = try dir.atomicFile(sub_path, .{ .mode = executable_mode }); | |
| 52 | const af = try dir.atomicFile(sub_path, .{ .mode = determineMode(module) }); | |
| 58 | 53 | defer af.deinit(); |
| 59 | 54 | |
| 60 | const result = try writeExecutableFile(allocator, module, af.file); | |
| 55 | const result = try writeFile(allocator, module, af.file); | |
| 61 | 56 | try af.finish(); |
| 62 | 57 | return result; |
| 63 | 58 | } |
| ... | ... | @@ -67,10 +62,10 @@ pub fn writeExecutableFilePath( |
| 67 | 62 | /// Returns an error if `file` is not already open with +read +write +seek abilities. |
| 68 | 63 | /// A malicious file is detected as incremental link failure and does not cause Illegal Behavior. |
| 69 | 64 | /// This operation is not atomic. |
| 70 | pub fn updateExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { | |
| 71 | return updateExecutableFileInner(allocator, module, file) catch |err| switch (err) { | |
| 65 | pub fn updateFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { | |
| 66 | return updateFileInner(allocator, module, file) catch |err| switch (err) { | |
| 72 | 67 | error.IncrFailed => { |
| 73 | return writeExecutableFile(allocator, module, file); | |
| 68 | return writeFile(allocator, module, file); | |
| 74 | 69 | }, |
| 75 | 70 | else => |e| return e, |
| 76 | 71 | }; |
| ... | ... | @@ -436,7 +431,7 @@ const Update = struct { |
| 436 | 431 | }, |
| 437 | 432 | } |
| 438 | 433 | } |
| 439 | if (self.entry_addr == null) { | |
| 434 | if (self.entry_addr == null and self.module.output_mode == .Exe) { | |
| 440 | 435 | const msg = try std.fmt.allocPrint(self.errors.allocator, "no entry point found", .{}); |
| 441 | 436 | errdefer self.errors.allocator.free(msg); |
| 442 | 437 | try self.errors.append(.{ |
| ... | ... | @@ -485,7 +480,15 @@ const Update = struct { |
| 485 | 480 | |
| 486 | 481 | assert(index == 16); |
| 487 | 482 | |
| 488 | mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf.ET.EXEC), endian); | |
| 483 | const elf_type = switch (self.module.output_mode) { | |
| 484 | .Exe => elf.ET.EXEC, | |
| 485 | .Obj => elf.ET.REL, | |
| 486 | .Lib => switch (self.module.link_mode) { | |
| 487 | .Static => elf.ET.REL, | |
| 488 | .Dynamic => elf.ET.DYN, | |
| 489 | }, | |
| 490 | }; | |
| 491 | mem.writeInt(u16, hdr_buf[index..][0..2], @enumToInt(elf_type), endian); | |
| 489 | 492 | index += 2; |
| 490 | 493 | |
| 491 | 494 | const machine = self.module.target.cpu.arch.toElfMachine(); |
| ... | ... | @@ -496,10 +499,11 @@ const Update = struct { |
| 496 | 499 | mem.writeInt(u32, hdr_buf[index..][0..4], 1, endian); |
| 497 | 500 | index += 4; |
| 498 | 501 | |
| 502 | const e_entry = if (elf_type == .REL) 0 else self.entry_addr.?; | |
| 503 | ||
| 499 | 504 | switch (ptr_width) { |
| 500 | 505 | .p32 => { |
| 501 | // e_entry | |
| 502 | mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, self.entry_addr.?), endian); | |
| 506 | mem.writeInt(u32, hdr_buf[index..][0..4], @intCast(u32, e_entry), endian); | |
| 503 | 507 | index += 4; |
| 504 | 508 | |
| 505 | 509 | // e_phoff |
| ... | ... | @@ -512,7 +516,7 @@ const Update = struct { |
| 512 | 516 | }, |
| 513 | 517 | .p64 => { |
| 514 | 518 | // e_entry |
| 515 | mem.writeInt(u64, hdr_buf[index..][0..8], self.entry_addr.?, endian); | |
| 519 | mem.writeInt(u64, hdr_buf[index..][0..8], e_entry, endian); | |
| 516 | 520 | index += 8; |
| 517 | 521 | |
| 518 | 522 | // e_phoff |
| ... | ... | @@ -750,7 +754,20 @@ const Update = struct { |
| 750 | 754 | |
| 751 | 755 | /// Truncates the existing file contents and overwrites the contents. |
| 752 | 756 | /// Returns an error if `file` is not already open with +read +write +seek abilities. |
| 753 | pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { | |
| 757 | pub fn writeFile(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { | |
| 758 | switch (module.output_mode) { | |
| 759 | .Exe => {}, | |
| 760 | .Obj => {}, | |
| 761 | .Lib => return error.TODOImplementWritingLibFiles, | |
| 762 | } | |
| 763 | switch (module.object_format) { | |
| 764 | .unknown => unreachable, // TODO remove this tag from the enum | |
| 765 | .coff => return error.TODOImplementWritingCOFF, | |
| 766 | .elf => {}, | |
| 767 | .macho => return error.TODOImplementWritingMachO, | |
| 768 | .wasm => return error.TODOImplementWritingWasmObjects, | |
| 769 | } | |
| 770 | ||
| 754 | 771 | var update = Update{ |
| 755 | 772 | .file = file, |
| 756 | 773 | .module = &module, |
| ... | ... | @@ -778,7 +795,7 @@ pub fn writeExecutableFile(allocator: *Allocator, module: ir.Module, file: fs.Fi |
| 778 | 795 | } |
| 779 | 796 | |
| 780 | 797 | /// Returns error.IncrFailed if incremental update could not be performed. |
| 781 | fn updateExecutableFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { | |
| 798 | fn updateFileInner(allocator: *Allocator, module: ir.Module, file: fs.File) !Result { | |
| 782 | 799 | //var ehdr_buf: [@sizeOf(elf.Elf64_Ehdr)]u8 = undefined; |
| 783 | 800 | |
| 784 | 801 | // TODO implement incremental linking |
| ... | ... | @@ -822,3 +839,19 @@ fn sectHeaderTo32(shdr: elf.Elf64_Shdr) elf.Elf32_Shdr { |
| 822 | 839 | .sh_entsize = @intCast(u32, shdr.sh_entsize), |
| 823 | 840 | }; |
| 824 | 841 | } |
| 842 | ||
| 843 | fn determineMode(module: ir.Module) fs.File.Mode { | |
| 844 | // On common systems with a 0o022 umask, 0o777 will still result in a file created | |
| 845 | // with 0o755 permissions, but it works appropriately if the system is configured | |
| 846 | // more leniently. As another data point, C's fopen seems to open files with the | |
| 847 | // 666 mode. | |
| 848 | const executable_mode = if (std.Target.current.os.tag == .windows) 0 else 0o777; | |
| 849 | switch (module.output_mode) { | |
| 850 | .Lib => return switch (module.link_mode) { | |
| 851 | .Dynamic => executable_mode, | |
| 852 | .Static => fs.File.default_mode, | |
| 853 | }, | |
| 854 | .Exe => return executable_mode, | |
| 855 | .Obj => return fs.File.default_mode, | |
| 856 | } | |
| 857 | } |
src-self-hosted/test.zig+209-198| ... | ... | @@ -1,237 +1,248 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | const mem = std.mem; | |
| 3 | const Target = std.Target; | |
| 4 | const Compilation = @import("compilation.zig").Compilation; | |
| 5 | const introspect = @import("introspect.zig"); | |
| 6 | const testing = std.testing; | |
| 7 | const errmsg = @import("errmsg.zig"); | |
| 8 | const ZigCompiler = @import("compilation.zig").ZigCompiler; | |
| 2 | const link = @import("link.zig"); | |
| 3 | const ir = @import("ir.zig"); | |
| 4 | const Allocator = std.mem.Allocator; | |
| 9 | 5 | |
| 10 | var ctx: TestContext = undefined; | |
| 6 | var global_ctx: TestContext = undefined; | |
| 11 | 7 | |
| 12 | test "stage2" { | |
| 13 | // TODO provide a way to run tests in evented I/O mode | |
| 14 | if (!std.io.is_async) return error.SkipZigTest; | |
| 8 | test "self-hosted" { | |
| 9 | try global_ctx.init(); | |
| 10 | defer global_ctx.deinit(); | |
| 15 | 11 | |
| 16 | // TODO https://github.com/ziglang/zig/issues/1364 | |
| 17 | // TODO https://github.com/ziglang/zig/issues/3117 | |
| 18 | if (true) return error.SkipZigTest; | |
| 12 | try @import("stage2_tests").addCases(&global_ctx); | |
| 19 | 13 | |
| 20 | try ctx.init(); | |
| 21 | defer ctx.deinit(); | |
| 22 | ||
| 23 | try @import("stage2_tests").addCases(&ctx); | |
| 24 | ||
| 25 | try ctx.run(); | |
| 14 | try global_ctx.run(); | |
| 26 | 15 | } |
| 27 | 16 | |
| 28 | const file1 = "1.zig"; | |
| 29 | // TODO https://github.com/ziglang/zig/issues/3783 | |
| 30 | const allocator = std.heap.page_allocator; | |
| 31 | ||
| 32 | 17 | pub const TestContext = struct { |
| 33 | zig_compiler: ZigCompiler, | |
| 34 | zig_lib_dir: []u8, | |
| 35 | file_index: std.atomic.Int(usize), | |
| 36 | group: std.event.Group(anyerror!void), | |
| 37 | any_err: anyerror!void, | |
| 18 | zir_cmp_output_cases: std.ArrayList(ZIRCompareOutputCase), | |
| 19 | zir_transform_cases: std.ArrayList(ZIRTransformCase), | |
| 20 | ||
| 21 | pub const ZIRCompareOutputCase = struct { | |
| 22 | name: []const u8, | |
| 23 | src: [:0]const u8, | |
| 24 | expected_stdout: []const u8, | |
| 25 | }; | |
| 26 | ||
| 27 | pub const ZIRTransformCase = struct { | |
| 28 | name: []const u8, | |
| 29 | src: [:0]const u8, | |
| 30 | expected_zir: []const u8, | |
| 31 | }; | |
| 32 | ||
| 33 | pub fn addZIRCompareOutput( | |
| 34 | ctx: *TestContext, | |
| 35 | name: []const u8, | |
| 36 | src: [:0]const u8, | |
| 37 | expected_stdout: []const u8, | |
| 38 | ) void { | |
| 39 | ctx.zir_cmp_output_cases.append(.{ | |
| 40 | .name = name, | |
| 41 | .src = src, | |
| 42 | .expected_stdout = expected_stdout, | |
| 43 | }) catch unreachable; | |
| 44 | } | |
| 38 | 45 | |
| 39 | const tmp_dir_name = "stage2_test_tmp"; | |
| 46 | pub fn addZIRTransform( | |
| 47 | ctx: *TestContext, | |
| 48 | name: []const u8, | |
| 49 | src: [:0]const u8, | |
| 50 | expected_zir: []const u8, | |
| 51 | ) void { | |
| 52 | ctx.zir_transform_cases.append(.{ | |
| 53 | .name = name, | |
| 54 | .src = src, | |
| 55 | .expected_zir = expected_zir, | |
| 56 | }) catch unreachable; | |
| 57 | } | |
| 40 | 58 | |
| 41 | 59 | fn init(self: *TestContext) !void { |
| 42 | self.* = TestContext{ | |
| 43 | .any_err = {}, | |
| 44 | .zig_compiler = undefined, | |
| 45 | .zig_lib_dir = undefined, | |
| 46 | .group = undefined, | |
| 47 | .file_index = std.atomic.Int(usize).init(0), | |
| 60 | self.* = .{ | |
| 61 | .zir_cmp_output_cases = std.ArrayList(ZIRCompareOutputCase).init(std.heap.page_allocator), | |
| 62 | .zir_transform_cases = std.ArrayList(ZIRTransformCase).init(std.heap.page_allocator), | |
| 48 | 63 | }; |
| 49 | ||
| 50 | self.zig_compiler = try ZigCompiler.init(allocator); | |
| 51 | errdefer self.zig_compiler.deinit(); | |
| 52 | ||
| 53 | self.group = std.event.Group(anyerror!void).init(allocator); | |
| 54 | errdefer self.group.wait() catch {}; | |
| 55 | ||
| 56 | self.zig_lib_dir = try introspect.resolveZigLibDir(allocator); | |
| 57 | errdefer allocator.free(self.zig_lib_dir); | |
| 58 | ||
| 59 | try std.fs.cwd().makePath(tmp_dir_name); | |
| 60 | errdefer std.fs.cwd().deleteTree(tmp_dir_name) catch {}; | |
| 61 | 64 | } |
| 62 | 65 | |
| 63 | 66 | fn deinit(self: *TestContext) void { |
| 64 | std.fs.cwd().deleteTree(tmp_dir_name) catch {}; | |
| 65 | allocator.free(self.zig_lib_dir); | |
| 66 | self.zig_compiler.deinit(); | |
| 67 | self.zir_cmp_output_cases.deinit(); | |
| 68 | self.zir_transform_cases.deinit(); | |
| 69 | self.* = undefined; | |
| 67 | 70 | } |
| 68 | 71 | |
| 69 | 72 | fn run(self: *TestContext) !void { |
| 70 | std.event.Loop.startCpuBoundOperation(); | |
| 71 | self.any_err = self.group.wait(); | |
| 72 | return self.any_err; | |
| 73 | var progress = std.Progress{}; | |
| 74 | const root_node = try progress.start("zir", self.zir_cmp_output_cases.items.len + | |
| 75 | self.zir_transform_cases.items.len); | |
| 76 | defer root_node.end(); | |
| 77 | ||
| 78 | const native_info = try std.zig.system.NativeTargetInfo.detect(std.heap.page_allocator, .{}); | |
| 79 | ||
| 80 | for (self.zir_cmp_output_cases.items) |case| { | |
| 81 | std.testing.base_allocator_instance.reset(); | |
| 82 | try self.runOneZIRCmpOutputCase(std.testing.allocator, root_node, case, native_info.target); | |
| 83 | try std.testing.allocator_instance.validate(); | |
| 84 | } | |
| 85 | for (self.zir_transform_cases.items) |case| { | |
| 86 | std.testing.base_allocator_instance.reset(); | |
| 87 | try self.runOneZIRTransformCase(std.testing.allocator, root_node, case, native_info.target); | |
| 88 | try std.testing.allocator_instance.validate(); | |
| 89 | } | |
| 73 | 90 | } |
| 74 | 91 | |
| 75 | fn testCompileError( | |
| 92 | fn runOneZIRCmpOutputCase( | |
| 76 | 93 | self: *TestContext, |
| 77 | source: []const u8, | |
| 78 | path: []const u8, | |
| 79 | line: usize, | |
| 80 | column: usize, | |
| 81 | msg: []const u8, | |
| 94 | allocator: *Allocator, | |
| 95 | root_node: *std.Progress.Node, | |
| 96 | case: ZIRCompareOutputCase, | |
| 97 | target: std.Target, | |
| 82 | 98 | ) !void { |
| 83 | var file_index_buf: [20]u8 = undefined; | |
| 84 | const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); | |
| 85 | const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); | |
| 99 | var tmp = std.testing.tmpDir(.{ .share_with_child_process = true }); | |
| 100 | defer tmp.cleanup(); | |
| 86 | 101 | |
| 87 | if (std.fs.path.dirname(file1_path)) |dirname| { | |
| 88 | try std.fs.cwd().makePath(dirname); | |
| 89 | } | |
| 102 | var prg_node = root_node.start(case.name, 4); | |
| 103 | prg_node.activate(); | |
| 104 | defer prg_node.end(); | |
| 90 | 105 | |
| 91 | try std.fs.cwd().writeFile(file1_path, source); | |
| 106 | var zir_module = x: { | |
| 107 | var parse_node = prg_node.start("parse", null); | |
| 108 | parse_node.activate(); | |
| 109 | defer parse_node.end(); | |
| 92 | 110 | |
| 93 | var comp = try Compilation.create( | |
| 94 | &self.zig_compiler, | |
| 95 | "test", | |
| 96 | file1_path, | |
| 97 | .Native, | |
| 98 | .Obj, | |
| 99 | .Debug, | |
| 100 | true, // is_static | |
| 101 | self.zig_lib_dir, | |
| 102 | ); | |
| 103 | errdefer comp.destroy(); | |
| 104 | ||
| 105 | comp.start(); | |
| 111 | break :x try ir.text.parse(allocator, case.src); | |
| 112 | }; | |
| 113 | defer zir_module.deinit(allocator); | |
| 114 | if (zir_module.errors.len != 0) { | |
| 115 | debugPrintErrors(case.src, zir_module.errors); | |
| 116 | return error.ParseFailure; | |
| 117 | } | |
| 106 | 118 | |
| 107 | try self.group.call(getModuleEvent, comp, source, path, line, column, msg); | |
| 108 | } | |
| 119 | var analyzed_module = x: { | |
| 120 | var analyze_node = prg_node.start("analyze", null); | |
| 121 | analyze_node.activate(); | |
| 122 | defer analyze_node.end(); | |
| 123 | ||
| 124 | break :x try ir.analyze(allocator, zir_module, .{ | |
| 125 | .target = target, | |
| 126 | .output_mode = .Exe, | |
| 127 | .link_mode = .Static, | |
| 128 | .optimize_mode = .Debug, | |
| 129 | }); | |
| 130 | }; | |
| 131 | defer analyzed_module.deinit(allocator); | |
| 132 | if (analyzed_module.errors.len != 0) { | |
| 133 | debugPrintErrors(case.src, analyzed_module.errors); | |
| 134 | return error.ParseFailure; | |
| 135 | } | |
| 109 | 136 | |
| 110 | fn testCompareOutputLibC( | |
| 111 | self: *TestContext, | |
| 112 | source: []const u8, | |
| 113 | expected_output: []const u8, | |
| 114 | ) !void { | |
| 115 | var file_index_buf: [20]u8 = undefined; | |
| 116 | const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", .{self.file_index.incr()}); | |
| 117 | const file1_path = try std.fs.path.join(allocator, [_][]const u8{ tmp_dir_name, file_index, file1 }); | |
| 137 | var link_result = x: { | |
| 138 | var link_node = prg_node.start("link", null); | |
| 139 | link_node.activate(); | |
| 140 | defer link_node.end(); | |
| 118 | 141 | |
| 119 | const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", .{ file1_path, (Target{ .Native = {} }).exeFileExt() }); | |
| 120 | if (std.fs.path.dirname(file1_path)) |dirname| { | |
| 121 | try std.fs.cwd().makePath(dirname); | |
| 142 | break :x try link.updateFilePath(allocator, analyzed_module, tmp.dir, "a.out"); | |
| 143 | }; | |
| 144 | defer link_result.deinit(allocator); | |
| 145 | if (link_result.errors.len != 0) { | |
| 146 | debugPrintErrors(case.src, link_result.errors); | |
| 147 | return error.LinkFailure; | |
| 122 | 148 | } |
| 123 | 149 | |
| 124 | try std.fs.cwd().writeFile(file1_path, source); | |
| 125 | ||
| 126 | var comp = try Compilation.create( | |
| 127 | &self.zig_compiler, | |
| 128 | "test", | |
| 129 | file1_path, | |
| 130 | .Native, | |
| 131 | .Exe, | |
| 132 | .Debug, | |
| 133 | false, | |
| 134 | self.zig_lib_dir, | |
| 135 | ); | |
| 136 | errdefer comp.destroy(); | |
| 137 | ||
| 138 | _ = try comp.addLinkLib("c", true); | |
| 139 | comp.link_out_file = output_file; | |
| 140 | comp.start(); | |
| 141 | ||
| 142 | try self.group.call(getModuleEventSuccess, comp, output_file, expected_output); | |
| 143 | } | |
| 150 | var exec_result = x: { | |
| 151 | var exec_node = prg_node.start("execute", null); | |
| 152 | exec_node.activate(); | |
| 153 | defer exec_node.end(); | |
| 144 | 154 | |
| 145 | async fn getModuleEventSuccess( | |
| 146 | comp: *Compilation, | |
| 147 | exe_file: []const u8, | |
| 148 | expected_output: []const u8, | |
| 149 | ) anyerror!void { | |
| 150 | defer comp.destroy(); | |
| 151 | const build_event = comp.events.get(); | |
| 152 | ||
| 153 | switch (build_event) { | |
| 154 | .Ok => { | |
| 155 | const argv = [_][]const u8{exe_file}; | |
| 156 | // TODO use event loop | |
| 157 | const child = try std.ChildProcess.exec(.{ | |
| 158 | .allocator = allocator, | |
| 159 | .argv = argv, | |
| 160 | .max_output_bytes = 1024 * 1024, | |
| 161 | }); | |
| 162 | switch (child.term) { | |
| 163 | .Exited => |code| { | |
| 164 | if (code != 0) { | |
| 165 | return error.BadReturnCode; | |
| 166 | } | |
| 167 | }, | |
| 168 | else => { | |
| 169 | return error.Crashed; | |
| 170 | }, | |
| 171 | } | |
| 172 | if (!mem.eql(u8, child.stdout, expected_output)) { | |
| 173 | return error.OutputMismatch; | |
| 174 | } | |
| 175 | }, | |
| 176 | .Error => @panic("Cannot return error: https://github.com/ziglang/zig/issues/3190"), // |err| return err, | |
| 177 | .Fail => |msgs| { | |
| 178 | const stderr = std.io.getStdErr(); | |
| 179 | try stderr.write("build incorrectly failed:\n"); | |
| 180 | for (msgs) |msg| { | |
| 181 | defer msg.destroy(); | |
| 182 | try msg.printToFile(stderr, .Auto); | |
| 155 | break :x try std.ChildProcess.exec(.{ | |
| 156 | .allocator = allocator, | |
| 157 | .argv = &[_][]const u8{"./a.out"}, | |
| 158 | .cwd_dir = tmp.dir, | |
| 159 | }); | |
| 160 | }; | |
| 161 | defer allocator.free(exec_result.stdout); | |
| 162 | defer allocator.free(exec_result.stderr); | |
| 163 | switch (exec_result.term) { | |
| 164 | .Exited => |code| { | |
| 165 | if (code != 0) { | |
| 166 | std.debug.warn("elf file exited with code {}\n", .{code}); | |
| 167 | return error.BinaryBadExitCode; | |
| 183 | 168 | } |
| 184 | 169 | }, |
| 170 | else => return error.BinaryCrashed, | |
| 185 | 171 | } |
| 172 | std.testing.expectEqualSlices(u8, case.expected_stdout, exec_result.stdout); | |
| 186 | 173 | } |
| 187 | 174 | |
| 188 | async fn getModuleEvent( | |
| 189 | comp: *Compilation, | |
| 190 | source: []const u8, | |
| 191 | path: []const u8, | |
| 192 | line: usize, | |
| 193 | column: usize, | |
| 194 | text: []const u8, | |
| 195 | ) anyerror!void { | |
| 196 | defer comp.destroy(); | |
| 197 | const build_event = comp.events.get(); | |
| 198 | ||
| 199 | switch (build_event) { | |
| 200 | .Ok => { | |
| 201 | @panic("build incorrectly succeeded"); | |
| 202 | }, | |
| 203 | .Error => |err| { | |
| 204 | @panic("build incorrectly failed"); | |
| 205 | }, | |
| 206 | .Fail => |msgs| { | |
| 207 | testing.expect(msgs.len != 0); | |
| 208 | for (msgs) |msg| { | |
| 209 | if (mem.endsWith(u8, msg.realpath, path) and mem.eql(u8, msg.text, text)) { | |
| 210 | const span = msg.getSpan(); | |
| 211 | const first_token = msg.getTree().tokens.at(span.first); | |
| 212 | const last_token = msg.getTree().tokens.at(span.first); | |
| 213 | const start_loc = msg.getTree().tokenLocationPtr(0, first_token); | |
| 214 | if (start_loc.line + 1 == line and start_loc.column + 1 == column) { | |
| 215 | return; | |
| 216 | } | |
| 217 | } | |
| 218 | } | |
| 219 | std.debug.warn("\n=====source:=======\n{}\n====expected:========\n{}:{}:{}: error: {}\n", .{ | |
| 220 | source, | |
| 221 | path, | |
| 222 | line, | |
| 223 | column, | |
| 224 | text, | |
| 225 | }); | |
| 226 | std.debug.warn("\n====found:========\n", .{}); | |
| 227 | const stderr = std.io.getStdErr(); | |
| 228 | for (msgs) |msg| { | |
| 229 | defer msg.destroy(); | |
| 230 | try msg.printToFile(stderr, errmsg.Color.Auto); | |
| 231 | } | |
| 232 | std.debug.warn("============\n", .{}); | |
| 233 | return error.TestFailed; | |
| 234 | }, | |
| 175 | fn runOneZIRTransformCase( | |
| 176 | self: *TestContext, | |
| 177 | allocator: *Allocator, | |
| 178 | root_node: *std.Progress.Node, | |
| 179 | case: ZIRTransformCase, | |
| 180 | target: std.Target, | |
| 181 | ) !void { | |
| 182 | var prg_node = root_node.start(case.name, 4); | |
| 183 | prg_node.activate(); | |
| 184 | defer prg_node.end(); | |
| 185 | ||
| 186 | var parse_node = prg_node.start("parse", null); | |
| 187 | parse_node.activate(); | |
| 188 | var zir_module = try ir.text.parse(allocator, case.src); | |
| 189 | defer zir_module.deinit(allocator); | |
| 190 | if (zir_module.errors.len != 0) { | |
| 191 | debugPrintErrors(case.src, zir_module.errors); | |
| 192 | return error.ParseFailure; | |
| 235 | 193 | } |
| 194 | parse_node.end(); | |
| 195 | ||
| 196 | var analyze_node = prg_node.start("analyze", null); | |
| 197 | analyze_node.activate(); | |
| 198 | var analyzed_module = try ir.analyze(allocator, zir_module, .{ | |
| 199 | .target = target, | |
| 200 | .output_mode = .Obj, | |
| 201 | .link_mode = .Static, | |
| 202 | .optimize_mode = .Debug, | |
| 203 | }); | |
| 204 | defer analyzed_module.deinit(allocator); | |
| 205 | if (analyzed_module.errors.len != 0) { | |
| 206 | debugPrintErrors(case.src, analyzed_module.errors); | |
| 207 | return error.ParseFailure; | |
| 208 | } | |
| 209 | analyze_node.end(); | |
| 210 | ||
| 211 | var emit_node = prg_node.start("emit", null); | |
| 212 | emit_node.activate(); | |
| 213 | var new_zir_module = try ir.text.emit_zir(allocator, analyzed_module); | |
| 214 | defer new_zir_module.deinit(allocator); | |
| 215 | emit_node.end(); | |
| 216 | ||
| 217 | var write_node = prg_node.start("write", null); | |
| 218 | write_node.activate(); | |
| 219 | var out_zir = std.ArrayList(u8).init(allocator); | |
| 220 | defer out_zir.deinit(); | |
| 221 | try new_zir_module.writeToStream(allocator, out_zir.outStream()); | |
| 222 | write_node.end(); | |
| 223 | ||
| 224 | std.testing.expectEqualSlices(u8, case.expected_zir, out_zir.items); | |
| 236 | 225 | } |
| 237 | 226 | }; |
| 227 | ||
| 228 | fn debugPrintErrors(src: []const u8, errors: var) void { | |
| 229 | std.debug.warn("\n", .{}); | |
| 230 | var nl = true; | |
| 231 | var line: usize = 1; | |
| 232 | for (src) |byte| { | |
| 233 | if (nl) { | |
| 234 | std.debug.warn("{: >3}| ", .{line}); | |
| 235 | nl = false; | |
| 236 | } | |
| 237 | if (byte == '\n') { | |
| 238 | nl = true; | |
| 239 | line += 1; | |
| 240 | } | |
| 241 | std.debug.warn("{c}", .{byte}); | |
| 242 | } | |
| 243 | std.debug.warn("\n", .{}); | |
| 244 | for (errors) |err_msg| { | |
| 245 | const loc = std.zig.findLineColumn(src, err_msg.byte_offset); | |
| 246 | std.debug.warn("{}:{}: error: {}\n", .{ loc.line + 1, loc.column + 1, err_msg.msg }); | |
| 247 | } | |
| 248 | } |
src-self-hosted/translate_c.zig+16-14| ... | ... | @@ -3913,18 +3913,20 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node { |
| 3913 | 3913 | }; |
| 3914 | 3914 | var aps_int = int; |
| 3915 | 3915 | const is_negative = ZigClangAPSInt_isSigned(int) and ZigClangAPSInt_isNegative(int); |
| 3916 | if (is_negative) | |
| 3917 | aps_int = ZigClangAPSInt_negate(aps_int); | |
| 3918 | var big = try math.big.Int.initCapacity(c.a(), num_limbs); | |
| 3919 | if (is_negative) | |
| 3920 | big.negate(); | |
| 3921 | defer big.deinit(); | |
| 3916 | if (is_negative) aps_int = ZigClangAPSInt_negate(aps_int); | |
| 3917 | defer if (is_negative) { | |
| 3918 | ZigClangAPSInt_free(aps_int); | |
| 3919 | }; | |
| 3920 | ||
| 3921 | const limbs = try c.a().alloc(math.big.Limb, num_limbs); | |
| 3922 | defer c.a().free(limbs); | |
| 3923 | ||
| 3922 | 3924 | const data = ZigClangAPSInt_getRawData(aps_int); |
| 3923 | switch (@sizeOf(std.math.big.Limb)) { | |
| 3925 | switch (@sizeOf(math.big.Limb)) { | |
| 3924 | 3926 | 8 => { |
| 3925 | 3927 | var i: usize = 0; |
| 3926 | 3928 | while (i < num_limbs) : (i += 1) { |
| 3927 | big.limbs[i] = data[i]; | |
| 3929 | limbs[i] = data[i]; | |
| 3928 | 3930 | } |
| 3929 | 3931 | }, |
| 3930 | 3932 | 4 => { |
| ... | ... | @@ -3934,23 +3936,23 @@ fn transCreateNodeAPInt(c: *Context, int: *const ZigClangAPSInt) !*ast.Node { |
| 3934 | 3936 | limb_i += 2; |
| 3935 | 3937 | data_i += 1; |
| 3936 | 3938 | }) { |
| 3937 | big.limbs[limb_i] = @truncate(u32, data[data_i]); | |
| 3938 | big.limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32); | |
| 3939 | limbs[limb_i] = @truncate(u32, data[data_i]); | |
| 3940 | limbs[limb_i + 1] = @truncate(u32, data[data_i] >> 32); | |
| 3939 | 3941 | } |
| 3940 | 3942 | }, |
| 3941 | 3943 | else => @compileError("unimplemented"), |
| 3942 | 3944 | } |
| 3943 | const str = big.toString(c.a(), 10, false) catch |err| switch (err) { | |
| 3945 | ||
| 3946 | const big: math.big.int.Const = .{ .limbs = limbs, .positive = !is_negative }; | |
| 3947 | const str = big.toStringAlloc(c.a(), 10, false) catch |err| switch (err) { | |
| 3944 | 3948 | error.OutOfMemory => return error.OutOfMemory, |
| 3945 | else => unreachable, | |
| 3946 | 3949 | }; |
| 3950 | defer c.a().free(str); | |
| 3947 | 3951 | const token = try appendToken(c, .IntegerLiteral, str); |
| 3948 | 3952 | const node = try c.a().create(ast.Node.IntegerLiteral); |
| 3949 | 3953 | node.* = .{ |
| 3950 | 3954 | .token = token, |
| 3951 | 3955 | }; |
| 3952 | if (is_negative) | |
| 3953 | ZigClangAPSInt_free(aps_int); | |
| 3954 | 3956 | return &node.base; |
| 3955 | 3957 | } |
| 3956 | 3958 |
src-self-hosted/type.zig+473-204| ... | ... | @@ -20,37 +20,40 @@ pub const Type = extern union { |
| 20 | 20 | |
| 21 | 21 | pub fn zigTypeTag(self: Type) std.builtin.TypeId { |
| 22 | 22 | switch (self.tag()) { |
| 23 | .@"u8", | |
| 24 | .@"i8", | |
| 25 | .@"isize", | |
| 26 | .@"usize", | |
| 27 | .@"c_short", | |
| 28 | .@"c_ushort", | |
| 29 | .@"c_int", | |
| 30 | .@"c_uint", | |
| 31 | .@"c_long", | |
| 32 | .@"c_ulong", | |
| 33 | .@"c_longlong", | |
| 34 | .@"c_ulonglong", | |
| 35 | .@"c_longdouble", | |
| 23 | .u8, | |
| 24 | .i8, | |
| 25 | .isize, | |
| 26 | .usize, | |
| 27 | .c_short, | |
| 28 | .c_ushort, | |
| 29 | .c_int, | |
| 30 | .c_uint, | |
| 31 | .c_long, | |
| 32 | .c_ulong, | |
| 33 | .c_longlong, | |
| 34 | .c_ulonglong, | |
| 35 | .c_longdouble, | |
| 36 | .int_signed, | |
| 37 | .int_unsigned, | |
| 36 | 38 | => return .Int, |
| 37 | 39 | |
| 38 | .@"f16", | |
| 39 | .@"f32", | |
| 40 | .@"f64", | |
| 41 | .@"f128", | |
| 40 | .f16, | |
| 41 | .f32, | |
| 42 | .f64, | |
| 43 | .f128, | |
| 42 | 44 | => return .Float, |
| 43 | 45 | |
| 44 | .@"c_void" => return .Opaque, | |
| 45 | .@"bool" => return .Bool, | |
| 46 | .@"void" => return .Void, | |
| 47 | .@"type" => return .Type, | |
| 48 | .@"anyerror" => return .ErrorSet, | |
| 49 | .@"comptime_int" => return .ComptimeInt, | |
| 50 | .@"comptime_float" => return .ComptimeFloat, | |
| 51 | .@"noreturn" => return .NoReturn, | |
| 46 | .c_void => return .Opaque, | |
| 47 | .bool => return .Bool, | |
| 48 | .void => return .Void, | |
| 49 | .type => return .Type, | |
| 50 | .anyerror => return .ErrorSet, | |
| 51 | .comptime_int => return .ComptimeInt, | |
| 52 | .comptime_float => return .ComptimeFloat, | |
| 53 | .noreturn => return .NoReturn, | |
| 52 | 54 | |
| 53 | 55 | .fn_naked_noreturn_no_args => return .Fn, |
| 56 | .fn_ccc_void_no_args => return .Fn, | |
| 54 | 57 | |
| 55 | 58 | .array, .array_u8_sentinel_0 => return .Array, |
| 56 | 59 | .single_const_pointer => return .Pointer, |
| ... | ... | @@ -153,35 +156,36 @@ pub const Type = extern union { |
| 153 | 156 | while (true) { |
| 154 | 157 | const t = ty.tag(); |
| 155 | 158 | switch (t) { |
| 156 | .@"u8", | |
| 157 | .@"i8", | |
| 158 | .@"isize", | |
| 159 | .@"usize", | |
| 160 | .@"c_short", | |
| 161 | .@"c_ushort", | |
| 162 | .@"c_int", | |
| 163 | .@"c_uint", | |
| 164 | .@"c_long", | |
| 165 | .@"c_ulong", | |
| 166 | .@"c_longlong", | |
| 167 | .@"c_ulonglong", | |
| 168 | .@"c_longdouble", | |
| 169 | .@"c_void", | |
| 170 | .@"f16", | |
| 171 | .@"f32", | |
| 172 | .@"f64", | |
| 173 | .@"f128", | |
| 174 | .@"bool", | |
| 175 | .@"void", | |
| 176 | .@"type", | |
| 177 | .@"anyerror", | |
| 178 | .@"comptime_int", | |
| 179 | .@"comptime_float", | |
| 180 | .@"noreturn", | |
| 159 | .u8, | |
| 160 | .i8, | |
| 161 | .isize, | |
| 162 | .usize, | |
| 163 | .c_short, | |
| 164 | .c_ushort, | |
| 165 | .c_int, | |
| 166 | .c_uint, | |
| 167 | .c_long, | |
| 168 | .c_ulong, | |
| 169 | .c_longlong, | |
| 170 | .c_ulonglong, | |
| 171 | .c_longdouble, | |
| 172 | .c_void, | |
| 173 | .f16, | |
| 174 | .f32, | |
| 175 | .f64, | |
| 176 | .f128, | |
| 177 | .bool, | |
| 178 | .void, | |
| 179 | .type, | |
| 180 | .anyerror, | |
| 181 | .comptime_int, | |
| 182 | .comptime_float, | |
| 183 | .noreturn, | |
| 181 | 184 | => return out_stream.writeAll(@tagName(t)), |
| 182 | 185 | |
| 183 | 186 | .const_slice_u8 => return out_stream.writeAll("[]const u8"), |
| 184 | 187 | .fn_naked_noreturn_no_args => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), |
| 188 | .fn_ccc_void_no_args => return out_stream.writeAll("fn() callconv(.C) void"), | |
| 185 | 189 | .single_const_pointer_to_comptime_int => return out_stream.writeAll("*const comptime_int"), |
| 186 | 190 | |
| 187 | 191 | .array_u8_sentinel_0 => { |
| ... | ... | @@ -200,6 +204,14 @@ pub const Type = extern union { |
| 200 | 204 | ty = payload.pointee_type; |
| 201 | 205 | continue; |
| 202 | 206 | }, |
| 207 | .int_signed => { | |
| 208 | const payload = @fieldParentPtr(Payload.IntSigned, "base", ty.ptr_otherwise); | |
| 209 | return out_stream.print("i{}", .{payload.bits}); | |
| 210 | }, | |
| 211 | .int_unsigned => { | |
| 212 | const payload = @fieldParentPtr(Payload.IntUnsigned, "base", ty.ptr_otherwise); | |
| 213 | return out_stream.print("u{}", .{payload.bits}); | |
| 214 | }, | |
| 203 | 215 | } |
| 204 | 216 | unreachable; |
| 205 | 217 | } |
| ... | ... | @@ -207,32 +219,33 @@ pub const Type = extern union { |
| 207 | 219 | |
| 208 | 220 | pub fn toValue(self: Type, allocator: *Allocator) Allocator.Error!Value { |
| 209 | 221 | switch (self.tag()) { |
| 210 | .@"u8" => return Value.initTag(.u8_type), | |
| 211 | .@"i8" => return Value.initTag(.i8_type), | |
| 212 | .@"isize" => return Value.initTag(.isize_type), | |
| 213 | .@"usize" => return Value.initTag(.usize_type), | |
| 214 | .@"c_short" => return Value.initTag(.c_short_type), | |
| 215 | .@"c_ushort" => return Value.initTag(.c_ushort_type), | |
| 216 | .@"c_int" => return Value.initTag(.c_int_type), | |
| 217 | .@"c_uint" => return Value.initTag(.c_uint_type), | |
| 218 | .@"c_long" => return Value.initTag(.c_long_type), | |
| 219 | .@"c_ulong" => return Value.initTag(.c_ulong_type), | |
| 220 | .@"c_longlong" => return Value.initTag(.c_longlong_type), | |
| 221 | .@"c_ulonglong" => return Value.initTag(.c_ulonglong_type), | |
| 222 | .@"c_longdouble" => return Value.initTag(.c_longdouble_type), | |
| 223 | .@"c_void" => return Value.initTag(.c_void_type), | |
| 224 | .@"f16" => return Value.initTag(.f16_type), | |
| 225 | .@"f32" => return Value.initTag(.f32_type), | |
| 226 | .@"f64" => return Value.initTag(.f64_type), | |
| 227 | .@"f128" => return Value.initTag(.f128_type), | |
| 228 | .@"bool" => return Value.initTag(.bool_type), | |
| 229 | .@"void" => return Value.initTag(.void_type), | |
| 230 | .@"type" => return Value.initTag(.type_type), | |
| 231 | .@"anyerror" => return Value.initTag(.anyerror_type), | |
| 232 | .@"comptime_int" => return Value.initTag(.comptime_int_type), | |
| 233 | .@"comptime_float" => return Value.initTag(.comptime_float_type), | |
| 234 | .@"noreturn" => return Value.initTag(.noreturn_type), | |
| 222 | .u8 => return Value.initTag(.u8_type), | |
| 223 | .i8 => return Value.initTag(.i8_type), | |
| 224 | .isize => return Value.initTag(.isize_type), | |
| 225 | .usize => return Value.initTag(.usize_type), | |
| 226 | .c_short => return Value.initTag(.c_short_type), | |
| 227 | .c_ushort => return Value.initTag(.c_ushort_type), | |
| 228 | .c_int => return Value.initTag(.c_int_type), | |
| 229 | .c_uint => return Value.initTag(.c_uint_type), | |
| 230 | .c_long => return Value.initTag(.c_long_type), | |
| 231 | .c_ulong => return Value.initTag(.c_ulong_type), | |
| 232 | .c_longlong => return Value.initTag(.c_longlong_type), | |
| 233 | .c_ulonglong => return Value.initTag(.c_ulonglong_type), | |
| 234 | .c_longdouble => return Value.initTag(.c_longdouble_type), | |
| 235 | .c_void => return Value.initTag(.c_void_type), | |
| 236 | .f16 => return Value.initTag(.f16_type), | |
| 237 | .f32 => return Value.initTag(.f32_type), | |
| 238 | .f64 => return Value.initTag(.f64_type), | |
| 239 | .f128 => return Value.initTag(.f128_type), | |
| 240 | .bool => return Value.initTag(.bool_type), | |
| 241 | .void => return Value.initTag(.void_type), | |
| 242 | .type => return Value.initTag(.type_type), | |
| 243 | .anyerror => return Value.initTag(.anyerror_type), | |
| 244 | .comptime_int => return Value.initTag(.comptime_int_type), | |
| 245 | .comptime_float => return Value.initTag(.comptime_float_type), | |
| 246 | .noreturn => return Value.initTag(.noreturn_type), | |
| 235 | 247 | .fn_naked_noreturn_no_args => return Value.initTag(.fn_naked_noreturn_no_args_type), |
| 248 | .fn_ccc_void_no_args => return Value.initTag(.fn_ccc_void_no_args_type), | |
| 236 | 249 | .single_const_pointer_to_comptime_int => return Value.initTag(.single_const_pointer_to_comptime_int_type), |
| 237 | 250 | .const_slice_u8 => return Value.initTag(.const_slice_u8_type), |
| 238 | 251 | else => { |
| ... | ... | @@ -245,35 +258,38 @@ pub const Type = extern union { |
| 245 | 258 | |
| 246 | 259 | pub fn isSinglePointer(self: Type) bool { |
| 247 | 260 | return switch (self.tag()) { |
| 248 | .@"u8", | |
| 249 | .@"i8", | |
| 250 | .@"isize", | |
| 251 | .@"usize", | |
| 252 | .@"c_short", | |
| 253 | .@"c_ushort", | |
| 254 | .@"c_int", | |
| 255 | .@"c_uint", | |
| 256 | .@"c_long", | |
| 257 | .@"c_ulong", | |
| 258 | .@"c_longlong", | |
| 259 | .@"c_ulonglong", | |
| 260 | .@"c_longdouble", | |
| 261 | .@"f16", | |
| 262 | .@"f32", | |
| 263 | .@"f64", | |
| 264 | .@"f128", | |
| 265 | .@"c_void", | |
| 266 | .@"bool", | |
| 267 | .@"void", | |
| 268 | .@"type", | |
| 269 | .@"anyerror", | |
| 270 | .@"comptime_int", | |
| 271 | .@"comptime_float", | |
| 272 | .@"noreturn", | |
| 261 | .u8, | |
| 262 | .i8, | |
| 263 | .isize, | |
| 264 | .usize, | |
| 265 | .c_short, | |
| 266 | .c_ushort, | |
| 267 | .c_int, | |
| 268 | .c_uint, | |
| 269 | .c_long, | |
| 270 | .c_ulong, | |
| 271 | .c_longlong, | |
| 272 | .c_ulonglong, | |
| 273 | .c_longdouble, | |
| 274 | .f16, | |
| 275 | .f32, | |
| 276 | .f64, | |
| 277 | .f128, | |
| 278 | .c_void, | |
| 279 | .bool, | |
| 280 | .void, | |
| 281 | .type, | |
| 282 | .anyerror, | |
| 283 | .comptime_int, | |
| 284 | .comptime_float, | |
| 285 | .noreturn, | |
| 273 | 286 | .array, |
| 274 | 287 | .array_u8_sentinel_0, |
| 275 | 288 | .const_slice_u8, |
| 276 | 289 | .fn_naked_noreturn_no_args, |
| 290 | .fn_ccc_void_no_args, | |
| 291 | .int_unsigned, | |
| 292 | .int_signed, | |
| 277 | 293 | => false, |
| 278 | 294 | |
| 279 | 295 | .single_const_pointer, |
| ... | ... | @@ -284,36 +300,39 @@ pub const Type = extern union { |
| 284 | 300 | |
| 285 | 301 | pub fn isSlice(self: Type) bool { |
| 286 | 302 | return switch (self.tag()) { |
| 287 | .@"u8", | |
| 288 | .@"i8", | |
| 289 | .@"isize", | |
| 290 | .@"usize", | |
| 291 | .@"c_short", | |
| 292 | .@"c_ushort", | |
| 293 | .@"c_int", | |
| 294 | .@"c_uint", | |
| 295 | .@"c_long", | |
| 296 | .@"c_ulong", | |
| 297 | .@"c_longlong", | |
| 298 | .@"c_ulonglong", | |
| 299 | .@"c_longdouble", | |
| 300 | .@"f16", | |
| 301 | .@"f32", | |
| 302 | .@"f64", | |
| 303 | .@"f128", | |
| 304 | .@"c_void", | |
| 305 | .@"bool", | |
| 306 | .@"void", | |
| 307 | .@"type", | |
| 308 | .@"anyerror", | |
| 309 | .@"comptime_int", | |
| 310 | .@"comptime_float", | |
| 311 | .@"noreturn", | |
| 303 | .u8, | |
| 304 | .i8, | |
| 305 | .isize, | |
| 306 | .usize, | |
| 307 | .c_short, | |
| 308 | .c_ushort, | |
| 309 | .c_int, | |
| 310 | .c_uint, | |
| 311 | .c_long, | |
| 312 | .c_ulong, | |
| 313 | .c_longlong, | |
| 314 | .c_ulonglong, | |
| 315 | .c_longdouble, | |
| 316 | .f16, | |
| 317 | .f32, | |
| 318 | .f64, | |
| 319 | .f128, | |
| 320 | .c_void, | |
| 321 | .bool, | |
| 322 | .void, | |
| 323 | .type, | |
| 324 | .anyerror, | |
| 325 | .comptime_int, | |
| 326 | .comptime_float, | |
| 327 | .noreturn, | |
| 312 | 328 | .array, |
| 313 | 329 | .array_u8_sentinel_0, |
| 314 | 330 | .single_const_pointer, |
| 315 | 331 | .single_const_pointer_to_comptime_int, |
| 316 | 332 | .fn_naked_noreturn_no_args, |
| 333 | .fn_ccc_void_no_args, | |
| 334 | .int_unsigned, | |
| 335 | .int_signed, | |
| 317 | 336 | => false, |
| 318 | 337 | |
| 319 | 338 | .const_slice_u8 => true, |
| ... | ... | @@ -323,34 +342,37 @@ pub const Type = extern union { |
| 323 | 342 | /// Asserts the type is a pointer type. |
| 324 | 343 | pub fn pointerIsConst(self: Type) bool { |
| 325 | 344 | return switch (self.tag()) { |
| 326 | .@"u8", | |
| 327 | .@"i8", | |
| 328 | .@"isize", | |
| 329 | .@"usize", | |
| 330 | .@"c_short", | |
| 331 | .@"c_ushort", | |
| 332 | .@"c_int", | |
| 333 | .@"c_uint", | |
| 334 | .@"c_long", | |
| 335 | .@"c_ulong", | |
| 336 | .@"c_longlong", | |
| 337 | .@"c_ulonglong", | |
| 338 | .@"c_longdouble", | |
| 339 | .@"f16", | |
| 340 | .@"f32", | |
| 341 | .@"f64", | |
| 342 | .@"f128", | |
| 343 | .@"c_void", | |
| 344 | .@"bool", | |
| 345 | .@"void", | |
| 346 | .@"type", | |
| 347 | .@"anyerror", | |
| 348 | .@"comptime_int", | |
| 349 | .@"comptime_float", | |
| 350 | .@"noreturn", | |
| 345 | .u8, | |
| 346 | .i8, | |
| 347 | .isize, | |
| 348 | .usize, | |
| 349 | .c_short, | |
| 350 | .c_ushort, | |
| 351 | .c_int, | |
| 352 | .c_uint, | |
| 353 | .c_long, | |
| 354 | .c_ulong, | |
| 355 | .c_longlong, | |
| 356 | .c_ulonglong, | |
| 357 | .c_longdouble, | |
| 358 | .f16, | |
| 359 | .f32, | |
| 360 | .f64, | |
| 361 | .f128, | |
| 362 | .c_void, | |
| 363 | .bool, | |
| 364 | .void, | |
| 365 | .type, | |
| 366 | .anyerror, | |
| 367 | .comptime_int, | |
| 368 | .comptime_float, | |
| 369 | .noreturn, | |
| 351 | 370 | .array, |
| 352 | 371 | .array_u8_sentinel_0, |
| 353 | 372 | .fn_naked_noreturn_no_args, |
| 373 | .fn_ccc_void_no_args, | |
| 374 | .int_unsigned, | |
| 375 | .int_signed, | |
| 354 | 376 | => unreachable, |
| 355 | 377 | |
| 356 | 378 | .single_const_pointer, |
| ... | ... | @@ -363,32 +385,35 @@ pub const Type = extern union { |
| 363 | 385 | /// Asserts the type is a pointer or array type. |
| 364 | 386 | pub fn elemType(self: Type) Type { |
| 365 | 387 | return switch (self.tag()) { |
| 366 | .@"u8", | |
| 367 | .@"i8", | |
| 368 | .@"isize", | |
| 369 | .@"usize", | |
| 370 | .@"c_short", | |
| 371 | .@"c_ushort", | |
| 372 | .@"c_int", | |
| 373 | .@"c_uint", | |
| 374 | .@"c_long", | |
| 375 | .@"c_ulong", | |
| 376 | .@"c_longlong", | |
| 377 | .@"c_ulonglong", | |
| 378 | .@"c_longdouble", | |
| 379 | .@"f16", | |
| 380 | .@"f32", | |
| 381 | .@"f64", | |
| 382 | .@"f128", | |
| 383 | .@"c_void", | |
| 384 | .@"bool", | |
| 385 | .@"void", | |
| 386 | .@"type", | |
| 387 | .@"anyerror", | |
| 388 | .@"comptime_int", | |
| 389 | .@"comptime_float", | |
| 390 | .@"noreturn", | |
| 388 | .u8, | |
| 389 | .i8, | |
| 390 | .isize, | |
| 391 | .usize, | |
| 392 | .c_short, | |
| 393 | .c_ushort, | |
| 394 | .c_int, | |
| 395 | .c_uint, | |
| 396 | .c_long, | |
| 397 | .c_ulong, | |
| 398 | .c_longlong, | |
| 399 | .c_ulonglong, | |
| 400 | .c_longdouble, | |
| 401 | .f16, | |
| 402 | .f32, | |
| 403 | .f64, | |
| 404 | .f128, | |
| 405 | .c_void, | |
| 406 | .bool, | |
| 407 | .void, | |
| 408 | .type, | |
| 409 | .anyerror, | |
| 410 | .comptime_int, | |
| 411 | .comptime_float, | |
| 412 | .noreturn, | |
| 391 | 413 | .fn_naked_noreturn_no_args, |
| 414 | .fn_ccc_void_no_args, | |
| 415 | .int_unsigned, | |
| 416 | .int_signed, | |
| 392 | 417 | => unreachable, |
| 393 | 418 | |
| 394 | 419 | .array => self.cast(Payload.Array).?.elem_type, |
| ... | ... | @@ -398,7 +423,7 @@ pub const Type = extern union { |
| 398 | 423 | }; |
| 399 | 424 | } |
| 400 | 425 | |
| 401 | /// Asserts the type is an array. | |
| 426 | /// Asserts the type is an array or vector. | |
| 402 | 427 | pub fn arrayLen(self: Type) u64 { |
| 403 | 428 | return switch (self.tag()) { |
| 404 | 429 | .u8, |
| ... | ... | @@ -427,9 +452,12 @@ pub const Type = extern union { |
| 427 | 452 | .comptime_float, |
| 428 | 453 | .noreturn, |
| 429 | 454 | .fn_naked_noreturn_no_args, |
| 455 | .fn_ccc_void_no_args, | |
| 430 | 456 | .single_const_pointer, |
| 431 | 457 | .single_const_pointer_to_comptime_int, |
| 432 | 458 | .const_slice_u8, |
| 459 | .int_unsigned, | |
| 460 | .int_signed, | |
| 433 | 461 | => unreachable, |
| 434 | 462 | |
| 435 | 463 | .array => self.cast(Payload.Array).?.len, |
| ... | ... | @@ -437,23 +465,67 @@ pub const Type = extern union { |
| 437 | 465 | }; |
| 438 | 466 | } |
| 439 | 467 | |
| 468 | /// Returns true if and only if the type is a fixed-width, signed integer. | |
| 469 | pub fn isSignedInt(self: Type) bool { | |
| 470 | return switch (self.tag()) { | |
| 471 | .f16, | |
| 472 | .f32, | |
| 473 | .f64, | |
| 474 | .f128, | |
| 475 | .c_longdouble, | |
| 476 | .c_void, | |
| 477 | .bool, | |
| 478 | .void, | |
| 479 | .type, | |
| 480 | .anyerror, | |
| 481 | .comptime_int, | |
| 482 | .comptime_float, | |
| 483 | .noreturn, | |
| 484 | .fn_naked_noreturn_no_args, | |
| 485 | .fn_ccc_void_no_args, | |
| 486 | .array, | |
| 487 | .single_const_pointer, | |
| 488 | .single_const_pointer_to_comptime_int, | |
| 489 | .array_u8_sentinel_0, | |
| 490 | .const_slice_u8, | |
| 491 | .int_unsigned, | |
| 492 | .u8, | |
| 493 | .usize, | |
| 494 | .c_ushort, | |
| 495 | .c_uint, | |
| 496 | .c_ulong, | |
| 497 | .c_ulonglong, | |
| 498 | => false, | |
| 499 | ||
| 500 | .int_signed, | |
| 501 | .i8, | |
| 502 | .isize, | |
| 503 | .c_short, | |
| 504 | .c_int, | |
| 505 | .c_long, | |
| 506 | .c_longlong, | |
| 507 | => true, | |
| 508 | }; | |
| 509 | } | |
| 510 | ||
| 440 | 511 | /// Asserts the type is a fixed-width integer. |
| 441 | 512 | pub fn intInfo(self: Type, target: Target) struct { signed: bool, bits: u16 } { |
| 442 | 513 | return switch (self.tag()) { |
| 443 | .@"f16", | |
| 444 | .@"f32", | |
| 445 | .@"f64", | |
| 446 | .@"f128", | |
| 447 | .@"c_longdouble", | |
| 448 | .@"c_void", | |
| 449 | .@"bool", | |
| 450 | .@"void", | |
| 451 | .@"type", | |
| 452 | .@"anyerror", | |
| 453 | .@"comptime_int", | |
| 454 | .@"comptime_float", | |
| 455 | .@"noreturn", | |
| 514 | .f16, | |
| 515 | .f32, | |
| 516 | .f64, | |
| 517 | .f128, | |
| 518 | .c_longdouble, | |
| 519 | .c_void, | |
| 520 | .bool, | |
| 521 | .void, | |
| 522 | .type, | |
| 523 | .anyerror, | |
| 524 | .comptime_int, | |
| 525 | .comptime_float, | |
| 526 | .noreturn, | |
| 456 | 527 | .fn_naked_noreturn_no_args, |
| 528 | .fn_ccc_void_no_args, | |
| 457 | 529 | .array, |
| 458 | 530 | .single_const_pointer, |
| 459 | 531 | .single_const_pointer_to_comptime_int, |
| ... | ... | @@ -461,18 +533,46 @@ pub const Type = extern union { |
| 461 | 533 | .const_slice_u8, |
| 462 | 534 | => unreachable, |
| 463 | 535 | |
| 464 | .@"u8" => .{ .signed = false, .bits = 8 }, | |
| 465 | .@"i8" => .{ .signed = true, .bits = 8 }, | |
| 466 | .@"usize" => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() }, | |
| 467 | .@"isize" => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() }, | |
| 468 | .@"c_short" => .{ .signed = true, .bits = CInteger.short.sizeInBits(target) }, | |
| 469 | .@"c_ushort" => .{ .signed = false, .bits = CInteger.ushort.sizeInBits(target) }, | |
| 470 | .@"c_int" => .{ .signed = true, .bits = CInteger.int.sizeInBits(target) }, | |
| 471 | .@"c_uint" => .{ .signed = false, .bits = CInteger.uint.sizeInBits(target) }, | |
| 472 | .@"c_long" => .{ .signed = true, .bits = CInteger.long.sizeInBits(target) }, | |
| 473 | .@"c_ulong" => .{ .signed = false, .bits = CInteger.ulong.sizeInBits(target) }, | |
| 474 | .@"c_longlong" => .{ .signed = true, .bits = CInteger.longlong.sizeInBits(target) }, | |
| 475 | .@"c_ulonglong" => .{ .signed = false, .bits = CInteger.ulonglong.sizeInBits(target) }, | |
| 536 | .int_unsigned => .{ .signed = false, .bits = self.cast(Payload.IntUnsigned).?.bits }, | |
| 537 | .int_signed => .{ .signed = true, .bits = self.cast(Payload.IntSigned).?.bits }, | |
| 538 | .u8 => .{ .signed = false, .bits = 8 }, | |
| 539 | .i8 => .{ .signed = true, .bits = 8 }, | |
| 540 | .usize => .{ .signed = false, .bits = target.cpu.arch.ptrBitWidth() }, | |
| 541 | .isize => .{ .signed = true, .bits = target.cpu.arch.ptrBitWidth() }, | |
| 542 | .c_short => .{ .signed = true, .bits = CType.short.sizeInBits(target) }, | |
| 543 | .c_ushort => .{ .signed = false, .bits = CType.ushort.sizeInBits(target) }, | |
| 544 | .c_int => .{ .signed = true, .bits = CType.int.sizeInBits(target) }, | |
| 545 | .c_uint => .{ .signed = false, .bits = CType.uint.sizeInBits(target) }, | |
| 546 | .c_long => .{ .signed = true, .bits = CType.long.sizeInBits(target) }, | |
| 547 | .c_ulong => .{ .signed = false, .bits = CType.ulong.sizeInBits(target) }, | |
| 548 | .c_longlong => .{ .signed = true, .bits = CType.longlong.sizeInBits(target) }, | |
| 549 | .c_ulonglong => .{ .signed = false, .bits = CType.ulonglong.sizeInBits(target) }, | |
| 550 | }; | |
| 551 | } | |
| 552 | ||
| 553 | pub fn isFloat(self: Type) bool { | |
| 554 | return switch (self.tag()) { | |
| 555 | .f16, | |
| 556 | .f32, | |
| 557 | .f64, | |
| 558 | .f128, | |
| 559 | .c_longdouble, | |
| 560 | => true, | |
| 561 | ||
| 562 | else => false, | |
| 563 | }; | |
| 564 | } | |
| 565 | ||
| 566 | /// Asserts the type is a fixed-size float. | |
| 567 | pub fn floatBits(self: Type, target: Target) u16 { | |
| 568 | return switch (self.tag()) { | |
| 569 | .f16 => 16, | |
| 570 | .f32 => 32, | |
| 571 | .f64 => 64, | |
| 572 | .f128 => 128, | |
| 573 | .c_longdouble => CType.longdouble.sizeInBits(target), | |
| 574 | ||
| 575 | else => unreachable, | |
| 476 | 576 | }; |
| 477 | 577 | } |
| 478 | 578 | |
| ... | ... | @@ -480,6 +580,7 @@ pub const Type = extern union { |
| 480 | 580 | pub fn fnParamLen(self: Type) usize { |
| 481 | 581 | return switch (self.tag()) { |
| 482 | 582 | .fn_naked_noreturn_no_args => 0, |
| 583 | .fn_ccc_void_no_args => 0, | |
| 483 | 584 | |
| 484 | 585 | .f16, |
| 485 | 586 | .f32, |
| ... | ... | @@ -511,6 +612,8 @@ pub const Type = extern union { |
| 511 | 612 | .c_ulong, |
| 512 | 613 | .c_longlong, |
| 513 | 614 | .c_ulonglong, |
| 615 | .int_unsigned, | |
| 616 | .int_signed, | |
| 514 | 617 | => unreachable, |
| 515 | 618 | }; |
| 516 | 619 | } |
| ... | ... | @@ -520,6 +623,7 @@ pub const Type = extern union { |
| 520 | 623 | pub fn fnParamTypes(self: Type, types: []Type) void { |
| 521 | 624 | switch (self.tag()) { |
| 522 | 625 | .fn_naked_noreturn_no_args => return, |
| 626 | .fn_ccc_void_no_args => return, | |
| 523 | 627 | |
| 524 | 628 | .f16, |
| 525 | 629 | .f32, |
| ... | ... | @@ -551,6 +655,8 @@ pub const Type = extern union { |
| 551 | 655 | .c_ulong, |
| 552 | 656 | .c_longlong, |
| 553 | 657 | .c_ulonglong, |
| 658 | .int_unsigned, | |
| 659 | .int_signed, | |
| 554 | 660 | => unreachable, |
| 555 | 661 | } |
| 556 | 662 | } |
| ... | ... | @@ -559,6 +665,7 @@ pub const Type = extern union { |
| 559 | 665 | pub fn fnReturnType(self: Type) Type { |
| 560 | 666 | return switch (self.tag()) { |
| 561 | 667 | .fn_naked_noreturn_no_args => Type.initTag(.noreturn), |
| 668 | .fn_ccc_void_no_args => Type.initTag(.void), | |
| 562 | 669 | |
| 563 | 670 | .f16, |
| 564 | 671 | .f32, |
| ... | ... | @@ -590,6 +697,8 @@ pub const Type = extern union { |
| 590 | 697 | .c_ulong, |
| 591 | 698 | .c_longlong, |
| 592 | 699 | .c_ulonglong, |
| 700 | .int_unsigned, | |
| 701 | .int_signed, | |
| 593 | 702 | => unreachable, |
| 594 | 703 | }; |
| 595 | 704 | } |
| ... | ... | @@ -598,6 +707,7 @@ pub const Type = extern union { |
| 598 | 707 | pub fn fnCallingConvention(self: Type) std.builtin.CallingConvention { |
| 599 | 708 | return switch (self.tag()) { |
| 600 | 709 | .fn_naked_noreturn_no_args => .Naked, |
| 710 | .fn_ccc_void_no_args => .C, | |
| 601 | 711 | |
| 602 | 712 | .f16, |
| 603 | 713 | .f32, |
| ... | ... | @@ -629,10 +739,148 @@ pub const Type = extern union { |
| 629 | 739 | .c_ulong, |
| 630 | 740 | .c_longlong, |
| 631 | 741 | .c_ulonglong, |
| 742 | .int_unsigned, | |
| 743 | .int_signed, | |
| 632 | 744 | => unreachable, |
| 633 | 745 | }; |
| 634 | 746 | } |
| 635 | 747 | |
| 748 | pub fn isNumeric(self: Type) bool { | |
| 749 | return switch (self.tag()) { | |
| 750 | .f16, | |
| 751 | .f32, | |
| 752 | .f64, | |
| 753 | .f128, | |
| 754 | .c_longdouble, | |
| 755 | .comptime_int, | |
| 756 | .comptime_float, | |
| 757 | .u8, | |
| 758 | .i8, | |
| 759 | .usize, | |
| 760 | .isize, | |
| 761 | .c_short, | |
| 762 | .c_ushort, | |
| 763 | .c_int, | |
| 764 | .c_uint, | |
| 765 | .c_long, | |
| 766 | .c_ulong, | |
| 767 | .c_longlong, | |
| 768 | .c_ulonglong, | |
| 769 | .int_unsigned, | |
| 770 | .int_signed, | |
| 771 | => true, | |
| 772 | ||
| 773 | .c_void, | |
| 774 | .bool, | |
| 775 | .void, | |
| 776 | .type, | |
| 777 | .anyerror, | |
| 778 | .noreturn, | |
| 779 | .fn_naked_noreturn_no_args, | |
| 780 | .fn_ccc_void_no_args, | |
| 781 | .array, | |
| 782 | .single_const_pointer, | |
| 783 | .single_const_pointer_to_comptime_int, | |
| 784 | .array_u8_sentinel_0, | |
| 785 | .const_slice_u8, | |
| 786 | => false, | |
| 787 | }; | |
| 788 | } | |
| 789 | ||
| 790 | pub fn onePossibleValue(self: Type) bool { | |
| 791 | var ty = self; | |
| 792 | while (true) switch (ty.tag()) { | |
| 793 | .f16, | |
| 794 | .f32, | |
| 795 | .f64, | |
| 796 | .f128, | |
| 797 | .c_longdouble, | |
| 798 | .comptime_int, | |
| 799 | .comptime_float, | |
| 800 | .u8, | |
| 801 | .i8, | |
| 802 | .usize, | |
| 803 | .isize, | |
| 804 | .c_short, | |
| 805 | .c_ushort, | |
| 806 | .c_int, | |
| 807 | .c_uint, | |
| 808 | .c_long, | |
| 809 | .c_ulong, | |
| 810 | .c_longlong, | |
| 811 | .c_ulonglong, | |
| 812 | .bool, | |
| 813 | .type, | |
| 814 | .anyerror, | |
| 815 | .fn_naked_noreturn_no_args, | |
| 816 | .fn_ccc_void_no_args, | |
| 817 | .single_const_pointer_to_comptime_int, | |
| 818 | .array_u8_sentinel_0, | |
| 819 | .const_slice_u8, | |
| 820 | => return false, | |
| 821 | ||
| 822 | .c_void, | |
| 823 | .void, | |
| 824 | .noreturn, | |
| 825 | => return true, | |
| 826 | ||
| 827 | .int_unsigned => return ty.cast(Payload.IntUnsigned).?.bits == 0, | |
| 828 | .int_signed => return ty.cast(Payload.IntSigned).?.bits == 0, | |
| 829 | .array => { | |
| 830 | const array = ty.cast(Payload.Array).?; | |
| 831 | if (array.len == 0) | |
| 832 | return true; | |
| 833 | ty = array.elem_type; | |
| 834 | continue; | |
| 835 | }, | |
| 836 | .single_const_pointer => { | |
| 837 | const ptr = ty.cast(Payload.SingleConstPointer).?; | |
| 838 | ty = ptr.pointee_type; | |
| 839 | continue; | |
| 840 | }, | |
| 841 | }; | |
| 842 | } | |
| 843 | ||
| 844 | pub fn isCPtr(self: Type) bool { | |
| 845 | return switch (self.tag()) { | |
| 846 | .f16, | |
| 847 | .f32, | |
| 848 | .f64, | |
| 849 | .f128, | |
| 850 | .c_longdouble, | |
| 851 | .comptime_int, | |
| 852 | .comptime_float, | |
| 853 | .u8, | |
| 854 | .i8, | |
| 855 | .usize, | |
| 856 | .isize, | |
| 857 | .c_short, | |
| 858 | .c_ushort, | |
| 859 | .c_int, | |
| 860 | .c_uint, | |
| 861 | .c_long, | |
| 862 | .c_ulong, | |
| 863 | .c_longlong, | |
| 864 | .c_ulonglong, | |
| 865 | .bool, | |
| 866 | .type, | |
| 867 | .anyerror, | |
| 868 | .fn_naked_noreturn_no_args, | |
| 869 | .fn_ccc_void_no_args, | |
| 870 | .single_const_pointer_to_comptime_int, | |
| 871 | .array_u8_sentinel_0, | |
| 872 | .const_slice_u8, | |
| 873 | .c_void, | |
| 874 | .void, | |
| 875 | .noreturn, | |
| 876 | .int_unsigned, | |
| 877 | .int_signed, | |
| 878 | .array, | |
| 879 | .single_const_pointer, | |
| 880 | => return false, | |
| 881 | }; | |
| 882 | } | |
| 883 | ||
| 636 | 884 | /// This enum does not directly correspond to `std.builtin.TypeId` because |
| 637 | 885 | /// it has extra enum tags in it, as a way of using less memory. For example, |
| 638 | 886 | /// even though Zig recognizes `*align(10) i32` and `*i32` both as Pointer types |
| ... | ... | @@ -667,6 +915,7 @@ pub const Type = extern union { |
| 667 | 915 | comptime_float, |
| 668 | 916 | noreturn, |
| 669 | 917 | fn_naked_noreturn_no_args, |
| 918 | fn_ccc_void_no_args, | |
| 670 | 919 | single_const_pointer_to_comptime_int, |
| 671 | 920 | const_slice_u8, // See last_no_payload_tag below. |
| 672 | 921 | // After this, the tag requires a payload. |
| ... | ... | @@ -674,6 +923,8 @@ pub const Type = extern union { |
| 674 | 923 | array_u8_sentinel_0, |
| 675 | 924 | array, |
| 676 | 925 | single_const_pointer, |
| 926 | int_signed, | |
| 927 | int_unsigned, | |
| 677 | 928 | |
| 678 | 929 | pub const last_no_payload_tag = Tag.const_slice_u8; |
| 679 | 930 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | ... | @@ -700,10 +951,22 @@ pub const Type = extern union { |
| 700 | 951 | |
| 701 | 952 | pointee_type: Type, |
| 702 | 953 | }; |
| 954 | ||
| 955 | pub const IntSigned = struct { | |
| 956 | base: Payload = Payload{ .tag = .int_signed }, | |
| 957 | ||
| 958 | bits: u16, | |
| 959 | }; | |
| 960 | ||
| 961 | pub const IntUnsigned = struct { | |
| 962 | base: Payload = Payload{ .tag = .int_unsigned }, | |
| 963 | ||
| 964 | bits: u16, | |
| 965 | }; | |
| 703 | 966 | }; |
| 704 | 967 | }; |
| 705 | 968 | |
| 706 | pub const CInteger = enum { | |
| 969 | pub const CType = enum { | |
| 707 | 970 | short, |
| 708 | 971 | ushort, |
| 709 | 972 | int, |
| ... | ... | @@ -712,8 +975,9 @@ pub const CInteger = enum { |
| 712 | 975 | ulong, |
| 713 | 976 | longlong, |
| 714 | 977 | ulonglong, |
| 978 | longdouble, | |
| 715 | 979 | |
| 716 | pub fn sizeInBits(self: CInteger, target: Target) u16 { | |
| 980 | pub fn sizeInBits(self: CType, target: Target) u16 { | |
| 717 | 981 | const arch = target.cpu.arch; |
| 718 | 982 | switch (target.os.tag) { |
| 719 | 983 | .freestanding, .other => switch (target.cpu.arch) { |
| ... | ... | @@ -729,6 +993,7 @@ pub const CInteger = enum { |
| 729 | 993 | .longlong, |
| 730 | 994 | .ulonglong, |
| 731 | 995 | => return 64, |
| 996 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | |
| 732 | 997 | }, |
| 733 | 998 | else => switch (self) { |
| 734 | 999 | .short, |
| ... | ... | @@ -743,6 +1008,7 @@ pub const CInteger = enum { |
| 743 | 1008 | .longlong, |
| 744 | 1009 | .ulonglong, |
| 745 | 1010 | => return 64, |
| 1011 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | |
| 746 | 1012 | }, |
| 747 | 1013 | }, |
| 748 | 1014 | |
| ... | ... | @@ -767,6 +1033,7 @@ pub const CInteger = enum { |
| 767 | 1033 | .longlong, |
| 768 | 1034 | .ulonglong, |
| 769 | 1035 | => return 64, |
| 1036 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | |
| 770 | 1037 | }, |
| 771 | 1038 | |
| 772 | 1039 | .windows, .uefi => switch (self) { |
| ... | ... | @@ -781,6 +1048,7 @@ pub const CInteger = enum { |
| 781 | 1048 | .longlong, |
| 782 | 1049 | .ulonglong, |
| 783 | 1050 | => return 64, |
| 1051 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | |
| 784 | 1052 | }, |
| 785 | 1053 | |
| 786 | 1054 | .ios => switch (self) { |
| ... | ... | @@ -795,6 +1063,7 @@ pub const CInteger = enum { |
| 795 | 1063 | .longlong, |
| 796 | 1064 | .ulonglong, |
| 797 | 1065 | => return 64, |
| 1066 | .longdouble => @panic("TODO figure out what kind of float `long double` is on this target"), | |
| 798 | 1067 | }, |
| 799 | 1068 | |
| 800 | 1069 | .ananas, |
| ... | ... | @@ -821,7 +1090,7 @@ pub const CInteger = enum { |
| 821 | 1090 | .amdpal, |
| 822 | 1091 | .hermit, |
| 823 | 1092 | .hurd, |
| 824 | => @panic("TODO specify the C integer type sizes for this OS"), | |
| 1093 | => @panic("TODO specify the C integer and float type sizes for this OS"), | |
| 825 | 1094 | } |
| 826 | 1095 | } |
| 827 | 1096 | }; |
src-self-hosted/value.zig+377-39| ... | ... | @@ -2,7 +2,8 @@ const std = @import("std"); |
| 2 | 2 | const Type = @import("type.zig").Type; |
| 3 | 3 | const log2 = std.math.log2; |
| 4 | 4 | const assert = std.debug.assert; |
| 5 | const BigInt = std.math.big.Int; | |
| 5 | const BigIntConst = std.math.big.int.Const; | |
| 6 | const BigIntMutable = std.math.big.int.Mutable; | |
| 6 | 7 | const Target = std.Target; |
| 7 | 8 | const Allocator = std.mem.Allocator; |
| 8 | 9 | |
| ... | ... | @@ -45,12 +46,14 @@ pub const Value = extern union { |
| 45 | 46 | comptime_float_type, |
| 46 | 47 | noreturn_type, |
| 47 | 48 | fn_naked_noreturn_no_args_type, |
| 49 | fn_ccc_void_no_args_type, | |
| 48 | 50 | single_const_pointer_to_comptime_int_type, |
| 49 | 51 | const_slice_u8_type, |
| 50 | 52 | |
| 53 | undef, | |
| 51 | 54 | zero, |
| 52 | void_value, | |
| 53 | noreturn_value, | |
| 55 | the_one_possible_value, // when the type only has one possible value | |
| 56 | null_value, | |
| 54 | 57 | bool_true, |
| 55 | 58 | bool_false, // See last_no_payload_tag below. |
| 56 | 59 | // After this, the tag requires a payload. |
| ... | ... | @@ -58,11 +61,13 @@ pub const Value = extern union { |
| 58 | 61 | ty, |
| 59 | 62 | int_u64, |
| 60 | 63 | int_i64, |
| 61 | int_big, | |
| 64 | int_big_positive, | |
| 65 | int_big_negative, | |
| 62 | 66 | function, |
| 63 | 67 | ref, |
| 64 | 68 | ref_val, |
| 65 | 69 | bytes, |
| 70 | repeated, // the value is a value repeated some number of times | |
| 66 | 71 | |
| 67 | 72 | pub const last_no_payload_tag = Tag.bool_false; |
| 68 | 73 | pub const no_payload_count = @enumToInt(last_no_payload_tag) + 1; |
| ... | ... | @@ -132,18 +137,21 @@ pub const Value = extern union { |
| 132 | 137 | .comptime_float_type => return out_stream.writeAll("comptime_float"), |
| 133 | 138 | .noreturn_type => return out_stream.writeAll("noreturn"), |
| 134 | 139 | .fn_naked_noreturn_no_args_type => return out_stream.writeAll("fn() callconv(.Naked) noreturn"), |
| 140 | .fn_ccc_void_no_args_type => return out_stream.writeAll("fn() callconv(.C) void"), | |
| 135 | 141 | .single_const_pointer_to_comptime_int_type => return out_stream.writeAll("*const comptime_int"), |
| 136 | 142 | .const_slice_u8_type => return out_stream.writeAll("[]const u8"), |
| 137 | 143 | |
| 144 | .null_value => return out_stream.writeAll("null"), | |
| 145 | .undef => return out_stream.writeAll("undefined"), | |
| 138 | 146 | .zero => return out_stream.writeAll("0"), |
| 139 | .void_value => return out_stream.writeAll("{}"), | |
| 140 | .noreturn_value => return out_stream.writeAll("unreachable"), | |
| 147 | .the_one_possible_value => return out_stream.writeAll("(one possible value)"), | |
| 141 | 148 | .bool_true => return out_stream.writeAll("true"), |
| 142 | 149 | .bool_false => return out_stream.writeAll("false"), |
| 143 | 150 | .ty => return val.cast(Payload.Ty).?.ty.format("", options, out_stream), |
| 144 | 151 | .int_u64 => return std.fmt.formatIntValue(val.cast(Payload.Int_u64).?.int, "", options, out_stream), |
| 145 | 152 | .int_i64 => return std.fmt.formatIntValue(val.cast(Payload.Int_i64).?.int, "", options, out_stream), |
| 146 | .int_big => return out_stream.print("{}", .{val.cast(Payload.IntBig).?.big_int}), | |
| 153 | .int_big_positive => return out_stream.print("{}", .{val.cast(Payload.IntBigPositive).?.asBigInt()}), | |
| 154 | .int_big_negative => return out_stream.print("{}", .{val.cast(Payload.IntBigNegative).?.asBigInt()}), | |
| 147 | 155 | .function => return out_stream.writeAll("(function)"), |
| 148 | 156 | .ref => return out_stream.writeAll("(ref)"), |
| 149 | 157 | .ref_val => { |
| ... | ... | @@ -152,6 +160,10 @@ pub const Value = extern union { |
| 152 | 160 | continue; |
| 153 | 161 | }, |
| 154 | 162 | .bytes => return std.zig.renderStringLiteral(self.cast(Payload.Bytes).?.data, out_stream), |
| 163 | .repeated => { | |
| 164 | try out_stream.writeAll("(repeated) "); | |
| 165 | val = val.cast(Payload.Repeated).?.val; | |
| 166 | }, | |
| 155 | 167 | }; |
| 156 | 168 | } |
| 157 | 169 | |
| ... | ... | @@ -195,27 +207,31 @@ pub const Value = extern union { |
| 195 | 207 | .comptime_float_type => Type.initTag(.@"comptime_float"), |
| 196 | 208 | .noreturn_type => Type.initTag(.@"noreturn"), |
| 197 | 209 | .fn_naked_noreturn_no_args_type => Type.initTag(.fn_naked_noreturn_no_args), |
| 210 | .fn_ccc_void_no_args_type => Type.initTag(.fn_ccc_void_no_args), | |
| 198 | 211 | .single_const_pointer_to_comptime_int_type => Type.initTag(.single_const_pointer_to_comptime_int), |
| 199 | 212 | .const_slice_u8_type => Type.initTag(.const_slice_u8), |
| 200 | 213 | |
| 214 | .undef, | |
| 201 | 215 | .zero, |
| 202 | .void_value, | |
| 203 | .noreturn_value, | |
| 216 | .the_one_possible_value, | |
| 204 | 217 | .bool_true, |
| 205 | 218 | .bool_false, |
| 219 | .null_value, | |
| 206 | 220 | .int_u64, |
| 207 | 221 | .int_i64, |
| 208 | .int_big, | |
| 222 | .int_big_positive, | |
| 223 | .int_big_negative, | |
| 209 | 224 | .function, |
| 210 | 225 | .ref, |
| 211 | 226 | .ref_val, |
| 212 | 227 | .bytes, |
| 228 | .repeated, | |
| 213 | 229 | => unreachable, |
| 214 | 230 | }; |
| 215 | 231 | } |
| 216 | 232 | |
| 217 | 233 | /// Asserts the value is an integer. |
| 218 | pub fn toBigInt(self: Value, allocator: *Allocator) Allocator.Error!BigInt { | |
| 234 | pub fn toBigInt(self: Value, space: *BigIntSpace) BigIntConst { | |
| 219 | 235 | switch (self.tag()) { |
| 220 | 236 | .ty, |
| 221 | 237 | .u8_type, |
| ... | ... | @@ -244,23 +260,28 @@ pub const Value = extern union { |
| 244 | 260 | .comptime_float_type, |
| 245 | 261 | .noreturn_type, |
| 246 | 262 | .fn_naked_noreturn_no_args_type, |
| 263 | .fn_ccc_void_no_args_type, | |
| 247 | 264 | .single_const_pointer_to_comptime_int_type, |
| 248 | 265 | .const_slice_u8_type, |
| 249 | .void_value, | |
| 250 | .noreturn_value, | |
| 251 | 266 | .bool_true, |
| 252 | 267 | .bool_false, |
| 268 | .null_value, | |
| 253 | 269 | .function, |
| 254 | 270 | .ref, |
| 255 | 271 | .ref_val, |
| 256 | 272 | .bytes, |
| 273 | .undef, | |
| 274 | .repeated, | |
| 257 | 275 | => unreachable, |
| 258 | 276 | |
| 259 | .zero => return BigInt.initSet(allocator, 0), | |
| 277 | .the_one_possible_value, // An integer with one possible value is always zero. | |
| 278 | .zero, | |
| 279 | => return BigIntMutable.init(&space.limbs, 0).toConst(), | |
| 260 | 280 | |
| 261 | .int_u64 => return BigInt.initSet(allocator, self.cast(Payload.Int_u64).?.int), | |
| 262 | .int_i64 => return BigInt.initSet(allocator, self.cast(Payload.Int_i64).?.int), | |
| 263 | .int_big => return self.cast(Payload.IntBig).?.big_int, | |
| 281 | .int_u64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_u64).?.int).toConst(), | |
| 282 | .int_i64 => return BigIntMutable.init(&space.limbs, self.cast(Payload.Int_i64).?.int).toConst(), | |
| 283 | .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt(), | |
| 284 | .int_big_negative => return self.cast(Payload.IntBigPositive).?.asBigInt(), | |
| 264 | 285 | } |
| 265 | 286 | } |
| 266 | 287 | |
| ... | ... | @@ -294,23 +315,90 @@ pub const Value = extern union { |
| 294 | 315 | .comptime_float_type, |
| 295 | 316 | .noreturn_type, |
| 296 | 317 | .fn_naked_noreturn_no_args_type, |
| 318 | .fn_ccc_void_no_args_type, | |
| 297 | 319 | .single_const_pointer_to_comptime_int_type, |
| 298 | 320 | .const_slice_u8_type, |
| 299 | .void_value, | |
| 300 | .noreturn_value, | |
| 301 | 321 | .bool_true, |
| 302 | 322 | .bool_false, |
| 323 | .null_value, | |
| 303 | 324 | .function, |
| 304 | 325 | .ref, |
| 305 | 326 | .ref_val, |
| 306 | 327 | .bytes, |
| 328 | .undef, | |
| 329 | .repeated, | |
| 307 | 330 | => unreachable, |
| 308 | 331 | |
| 309 | .zero => return 0, | |
| 332 | .zero, | |
| 333 | .the_one_possible_value, // an integer with one possible value is always zero | |
| 334 | => return 0, | |
| 310 | 335 | |
| 311 | 336 | .int_u64 => return self.cast(Payload.Int_u64).?.int, |
| 312 | 337 | .int_i64 => return @intCast(u64, self.cast(Payload.Int_u64).?.int), |
| 313 | .int_big => return self.cast(Payload.IntBig).?.big_int.to(u64) catch unreachable, | |
| 338 | .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().to(u64) catch unreachable, | |
| 339 | .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().to(u64) catch unreachable, | |
| 340 | } | |
| 341 | } | |
| 342 | ||
| 343 | /// Asserts the value is an integer and not undefined. | |
| 344 | /// Returns the number of bits the value requires to represent stored in twos complement form. | |
| 345 | pub fn intBitCountTwosComp(self: Value) usize { | |
| 346 | switch (self.tag()) { | |
| 347 | .ty, | |
| 348 | .u8_type, | |
| 349 | .i8_type, | |
| 350 | .isize_type, | |
| 351 | .usize_type, | |
| 352 | .c_short_type, | |
| 353 | .c_ushort_type, | |
| 354 | .c_int_type, | |
| 355 | .c_uint_type, | |
| 356 | .c_long_type, | |
| 357 | .c_ulong_type, | |
| 358 | .c_longlong_type, | |
| 359 | .c_ulonglong_type, | |
| 360 | .c_longdouble_type, | |
| 361 | .f16_type, | |
| 362 | .f32_type, | |
| 363 | .f64_type, | |
| 364 | .f128_type, | |
| 365 | .c_void_type, | |
| 366 | .bool_type, | |
| 367 | .void_type, | |
| 368 | .type_type, | |
| 369 | .anyerror_type, | |
| 370 | .comptime_int_type, | |
| 371 | .comptime_float_type, | |
| 372 | .noreturn_type, | |
| 373 | .fn_naked_noreturn_no_args_type, | |
| 374 | .fn_ccc_void_no_args_type, | |
| 375 | .single_const_pointer_to_comptime_int_type, | |
| 376 | .const_slice_u8_type, | |
| 377 | .bool_true, | |
| 378 | .bool_false, | |
| 379 | .null_value, | |
| 380 | .function, | |
| 381 | .ref, | |
| 382 | .ref_val, | |
| 383 | .bytes, | |
| 384 | .undef, | |
| 385 | .repeated, | |
| 386 | => unreachable, | |
| 387 | ||
| 388 | .the_one_possible_value, // an integer with one possible value is always zero | |
| 389 | .zero, | |
| 390 | => return 0, | |
| 391 | ||
| 392 | .int_u64 => { | |
| 393 | const x = self.cast(Payload.Int_u64).?.int; | |
| 394 | if (x == 0) return 0; | |
| 395 | return std.math.log2(x) + 1; | |
| 396 | }, | |
| 397 | .int_i64 => { | |
| 398 | @panic("TODO implement i64 intBitCountTwosComp"); | |
| 399 | }, | |
| 400 | .int_big_positive => return self.cast(Payload.IntBigPositive).?.asBigInt().bitCountTwosComp(), | |
| 401 | .int_big_negative => return self.cast(Payload.IntBigNegative).?.asBigInt().bitCountTwosComp(), | |
| 314 | 402 | } |
| 315 | 403 | } |
| 316 | 404 | |
| ... | ... | @@ -344,19 +432,23 @@ pub const Value = extern union { |
| 344 | 432 | .comptime_float_type, |
| 345 | 433 | .noreturn_type, |
| 346 | 434 | .fn_naked_noreturn_no_args_type, |
| 435 | .fn_ccc_void_no_args_type, | |
| 347 | 436 | .single_const_pointer_to_comptime_int_type, |
| 348 | 437 | .const_slice_u8_type, |
| 349 | .void_value, | |
| 350 | .noreturn_value, | |
| 351 | 438 | .bool_true, |
| 352 | 439 | .bool_false, |
| 440 | .null_value, | |
| 353 | 441 | .function, |
| 354 | 442 | .ref, |
| 355 | 443 | .ref_val, |
| 356 | 444 | .bytes, |
| 445 | .repeated, | |
| 357 | 446 | => unreachable, |
| 358 | 447 | |
| 359 | .zero => return true, | |
| 448 | .zero, | |
| 449 | .undef, | |
| 450 | .the_one_possible_value, // an integer with one possible value is always zero | |
| 451 | => return true, | |
| 360 | 452 | |
| 361 | 453 | .int_u64 => switch (ty.zigTypeTag()) { |
| 362 | 454 | .Int => { |
| ... | ... | @@ -381,20 +473,171 @@ pub const Value = extern union { |
| 381 | 473 | .ComptimeInt => return true, |
| 382 | 474 | else => unreachable, |
| 383 | 475 | }, |
| 384 | .int_big => switch (ty.zigTypeTag()) { | |
| 476 | .int_big_positive => switch (ty.zigTypeTag()) { | |
| 385 | 477 | .Int => { |
| 386 | 478 | const info = ty.intInfo(target); |
| 387 | return self.cast(Payload.IntBig).?.big_int.fitsInTwosComp(info.signed, info.bits); | |
| 479 | return self.cast(Payload.IntBigPositive).?.asBigInt().fitsInTwosComp(info.signed, info.bits); | |
| 388 | 480 | }, |
| 389 | 481 | .ComptimeInt => return true, |
| 390 | 482 | else => unreachable, |
| 391 | 483 | }, |
| 484 | .int_big_negative => switch (ty.zigTypeTag()) { | |
| 485 | .Int => { | |
| 486 | const info = ty.intInfo(target); | |
| 487 | return self.cast(Payload.IntBigNegative).?.asBigInt().fitsInTwosComp(info.signed, info.bits); | |
| 488 | }, | |
| 489 | .ComptimeInt => return true, | |
| 490 | else => unreachable, | |
| 491 | }, | |
| 492 | } | |
| 493 | } | |
| 494 | ||
| 495 | /// Asserts the value is a float | |
| 496 | pub fn floatHasFraction(self: Value) bool { | |
| 497 | return switch (self.tag()) { | |
| 498 | .ty, | |
| 499 | .u8_type, | |
| 500 | .i8_type, | |
| 501 | .isize_type, | |
| 502 | .usize_type, | |
| 503 | .c_short_type, | |
| 504 | .c_ushort_type, | |
| 505 | .c_int_type, | |
| 506 | .c_uint_type, | |
| 507 | .c_long_type, | |
| 508 | .c_ulong_type, | |
| 509 | .c_longlong_type, | |
| 510 | .c_ulonglong_type, | |
| 511 | .c_longdouble_type, | |
| 512 | .f16_type, | |
| 513 | .f32_type, | |
| 514 | .f64_type, | |
| 515 | .f128_type, | |
| 516 | .c_void_type, | |
| 517 | .bool_type, | |
| 518 | .void_type, | |
| 519 | .type_type, | |
| 520 | .anyerror_type, | |
| 521 | .comptime_int_type, | |
| 522 | .comptime_float_type, | |
| 523 | .noreturn_type, | |
| 524 | .fn_naked_noreturn_no_args_type, | |
| 525 | .fn_ccc_void_no_args_type, | |
| 526 | .single_const_pointer_to_comptime_int_type, | |
| 527 | .const_slice_u8_type, | |
| 528 | .bool_true, | |
| 529 | .bool_false, | |
| 530 | .null_value, | |
| 531 | .function, | |
| 532 | .ref, | |
| 533 | .ref_val, | |
| 534 | .bytes, | |
| 535 | .repeated, | |
| 536 | .undef, | |
| 537 | .int_u64, | |
| 538 | .int_i64, | |
| 539 | .int_big_positive, | |
| 540 | .int_big_negative, | |
| 541 | .the_one_possible_value, | |
| 542 | => unreachable, | |
| 543 | ||
| 544 | .zero => false, | |
| 545 | }; | |
| 546 | } | |
| 547 | ||
| 548 | pub fn orderAgainstZero(lhs: Value) std.math.Order { | |
| 549 | switch (lhs.tag()) { | |
| 550 | .ty, | |
| 551 | .u8_type, | |
| 552 | .i8_type, | |
| 553 | .isize_type, | |
| 554 | .usize_type, | |
| 555 | .c_short_type, | |
| 556 | .c_ushort_type, | |
| 557 | .c_int_type, | |
| 558 | .c_uint_type, | |
| 559 | .c_long_type, | |
| 560 | .c_ulong_type, | |
| 561 | .c_longlong_type, | |
| 562 | .c_ulonglong_type, | |
| 563 | .c_longdouble_type, | |
| 564 | .f16_type, | |
| 565 | .f32_type, | |
| 566 | .f64_type, | |
| 567 | .f128_type, | |
| 568 | .c_void_type, | |
| 569 | .bool_type, | |
| 570 | .void_type, | |
| 571 | .type_type, | |
| 572 | .anyerror_type, | |
| 573 | .comptime_int_type, | |
| 574 | .comptime_float_type, | |
| 575 | .noreturn_type, | |
| 576 | .fn_naked_noreturn_no_args_type, | |
| 577 | .fn_ccc_void_no_args_type, | |
| 578 | .single_const_pointer_to_comptime_int_type, | |
| 579 | .const_slice_u8_type, | |
| 580 | .bool_true, | |
| 581 | .bool_false, | |
| 582 | .null_value, | |
| 583 | .function, | |
| 584 | .ref, | |
| 585 | .ref_val, | |
| 586 | .bytes, | |
| 587 | .repeated, | |
| 588 | .undef, | |
| 589 | => unreachable, | |
| 590 | ||
| 591 | .zero, | |
| 592 | .the_one_possible_value, // an integer with one possible value is always zero | |
| 593 | => return .eq, | |
| 594 | ||
| 595 | .int_u64 => return std.math.order(lhs.cast(Payload.Int_u64).?.int, 0), | |
| 596 | .int_i64 => return std.math.order(lhs.cast(Payload.Int_i64).?.int, 0), | |
| 597 | .int_big_positive => return lhs.cast(Payload.IntBigPositive).?.asBigInt().orderAgainstScalar(0), | |
| 598 | .int_big_negative => return lhs.cast(Payload.IntBigNegative).?.asBigInt().orderAgainstScalar(0), | |
| 392 | 599 | } |
| 393 | 600 | } |
| 394 | 601 | |
| 602 | /// Asserts the value is comparable. | |
| 603 | pub fn order(lhs: Value, rhs: Value) std.math.Order { | |
| 604 | const lhs_tag = lhs.tag(); | |
| 605 | const rhs_tag = lhs.tag(); | |
| 606 | const lhs_is_zero = lhs_tag == .zero or lhs_tag == .the_one_possible_value; | |
| 607 | const rhs_is_zero = rhs_tag == .zero or rhs_tag == .the_one_possible_value; | |
| 608 | if (lhs_is_zero) return rhs.orderAgainstZero().invert(); | |
| 609 | if (rhs_is_zero) return lhs.orderAgainstZero(); | |
| 610 | ||
| 611 | // TODO floats | |
| 612 | ||
| 613 | var lhs_bigint_space: BigIntSpace = undefined; | |
| 614 | var rhs_bigint_space: BigIntSpace = undefined; | |
| 615 | const lhs_bigint = lhs.toBigInt(&lhs_bigint_space); | |
| 616 | const rhs_bigint = rhs.toBigInt(&rhs_bigint_space); | |
| 617 | return lhs_bigint.order(rhs_bigint); | |
| 618 | } | |
| 619 | ||
| 620 | /// Asserts the value is comparable. | |
| 621 | pub fn compare(lhs: Value, op: std.math.CompareOperator, rhs: Value) bool { | |
| 622 | return order(lhs, rhs).compare(op); | |
| 623 | } | |
| 624 | ||
| 625 | /// Asserts the value is comparable. | |
| 626 | pub fn compareWithZero(lhs: Value, op: std.math.CompareOperator) bool { | |
| 627 | return orderAgainstZero(lhs).compare(op); | |
| 628 | } | |
| 629 | ||
| 630 | pub fn toBool(self: Value) bool { | |
| 631 | return switch (self.tag()) { | |
| 632 | .bool_true => true, | |
| 633 | .bool_false => false, | |
| 634 | else => unreachable, | |
| 635 | }; | |
| 636 | } | |
| 637 | ||
| 395 | 638 | /// Asserts the value is a pointer and dereferences it. |
| 396 | 639 | pub fn pointerDeref(self: Value) Value { |
| 397 | switch (self.tag()) { | |
| 640 | return switch (self.tag()) { | |
| 398 | 641 | .ty, |
| 399 | 642 | .u8_type, |
| 400 | 643 | .i8_type, |
| ... | ... | @@ -422,23 +665,27 @@ pub const Value = extern union { |
| 422 | 665 | .comptime_float_type, |
| 423 | 666 | .noreturn_type, |
| 424 | 667 | .fn_naked_noreturn_no_args_type, |
| 668 | .fn_ccc_void_no_args_type, | |
| 425 | 669 | .single_const_pointer_to_comptime_int_type, |
| 426 | 670 | .const_slice_u8_type, |
| 427 | 671 | .zero, |
| 428 | .void_value, | |
| 429 | .noreturn_value, | |
| 430 | 672 | .bool_true, |
| 431 | 673 | .bool_false, |
| 674 | .null_value, | |
| 432 | 675 | .function, |
| 433 | 676 | .int_u64, |
| 434 | 677 | .int_i64, |
| 435 | .int_big, | |
| 678 | .int_big_positive, | |
| 679 | .int_big_negative, | |
| 436 | 680 | .bytes, |
| 681 | .undef, | |
| 682 | .repeated, | |
| 437 | 683 | => unreachable, |
| 438 | 684 | |
| 439 | .ref => return self.cast(Payload.Ref).?.cell.contents, | |
| 440 | .ref_val => return self.cast(Payload.RefVal).?.val, | |
| 441 | } | |
| 685 | .the_one_possible_value => Value.initTag(.the_one_possible_value), | |
| 686 | .ref => self.cast(Payload.Ref).?.cell.contents, | |
| 687 | .ref_val => self.cast(Payload.RefVal).?.val, | |
| 688 | }; | |
| 442 | 689 | } |
| 443 | 690 | |
| 444 | 691 | /// Asserts the value is a single-item pointer to an array, or an array, |
| ... | ... | @@ -472,17 +719,20 @@ pub const Value = extern union { |
| 472 | 719 | .comptime_float_type, |
| 473 | 720 | .noreturn_type, |
| 474 | 721 | .fn_naked_noreturn_no_args_type, |
| 722 | .fn_ccc_void_no_args_type, | |
| 475 | 723 | .single_const_pointer_to_comptime_int_type, |
| 476 | 724 | .const_slice_u8_type, |
| 477 | 725 | .zero, |
| 478 | .void_value, | |
| 479 | .noreturn_value, | |
| 726 | .the_one_possible_value, | |
| 480 | 727 | .bool_true, |
| 481 | 728 | .bool_false, |
| 729 | .null_value, | |
| 482 | 730 | .function, |
| 483 | 731 | .int_u64, |
| 484 | 732 | .int_i64, |
| 485 | .int_big, | |
| 733 | .int_big_positive, | |
| 734 | .int_big_negative, | |
| 735 | .undef, | |
| 486 | 736 | => unreachable, |
| 487 | 737 | |
| 488 | 738 | .ref => @panic("TODO figure out how MemoryCell works"), |
| ... | ... | @@ -493,9 +743,70 @@ pub const Value = extern union { |
| 493 | 743 | int_payload.* = .{ .int = self.cast(Payload.Bytes).?.data[index] }; |
| 494 | 744 | return Value.initPayload(&int_payload.base); |
| 495 | 745 | }, |
| 746 | ||
| 747 | // No matter the index; all the elements are the same! | |
| 748 | .repeated => return self.cast(Payload.Repeated).?.val, | |
| 496 | 749 | } |
| 497 | 750 | } |
| 498 | 751 | |
| 752 | pub fn isUndef(self: Value) bool { | |
| 753 | return self.tag() == .undef; | |
| 754 | } | |
| 755 | ||
| 756 | /// Valid for all types. Asserts the value is not undefined. | |
| 757 | /// `.the_one_possible_value` is reported as not null. | |
| 758 | pub fn isNull(self: Value) bool { | |
| 759 | return switch (self.tag()) { | |
| 760 | .ty, | |
| 761 | .u8_type, | |
| 762 | .i8_type, | |
| 763 | .isize_type, | |
| 764 | .usize_type, | |
| 765 | .c_short_type, | |
| 766 | .c_ushort_type, | |
| 767 | .c_int_type, | |
| 768 | .c_uint_type, | |
| 769 | .c_long_type, | |
| 770 | .c_ulong_type, | |
| 771 | .c_longlong_type, | |
| 772 | .c_ulonglong_type, | |
| 773 | .c_longdouble_type, | |
| 774 | .f16_type, | |
| 775 | .f32_type, | |
| 776 | .f64_type, | |
| 777 | .f128_type, | |
| 778 | .c_void_type, | |
| 779 | .bool_type, | |
| 780 | .void_type, | |
| 781 | .type_type, | |
| 782 | .anyerror_type, | |
| 783 | .comptime_int_type, | |
| 784 | .comptime_float_type, | |
| 785 | .noreturn_type, | |
| 786 | .fn_naked_noreturn_no_args_type, | |
| 787 | .fn_ccc_void_no_args_type, | |
| 788 | .single_const_pointer_to_comptime_int_type, | |
| 789 | .const_slice_u8_type, | |
| 790 | .zero, | |
| 791 | .the_one_possible_value, | |
| 792 | .bool_true, | |
| 793 | .bool_false, | |
| 794 | .function, | |
| 795 | .int_u64, | |
| 796 | .int_i64, | |
| 797 | .int_big_positive, | |
| 798 | .int_big_negative, | |
| 799 | .ref, | |
| 800 | .ref_val, | |
| 801 | .bytes, | |
| 802 | .repeated, | |
| 803 | => false, | |
| 804 | ||
| 805 | .undef => unreachable, | |
| 806 | .null_value => true, | |
| 807 | }; | |
| 808 | } | |
| 809 | ||
| 499 | 810 | /// This type is not copyable since it may contain pointers to its inner data. |
| 500 | 811 | pub const Payload = struct { |
| 501 | 812 | tag: Tag, |
| ... | ... | @@ -510,9 +821,22 @@ pub const Value = extern union { |
| 510 | 821 | int: i64, |
| 511 | 822 | }; |
| 512 | 823 | |
| 513 | pub const IntBig = struct { | |
| 514 | base: Payload = Payload{ .tag = .int_big }, | |
| 515 | big_int: BigInt, | |
| 824 | pub const IntBigPositive = struct { | |
| 825 | base: Payload = Payload{ .tag = .int_big_positive }, | |
| 826 | limbs: []const std.math.big.Limb, | |
| 827 | ||
| 828 | pub fn asBigInt(self: IntBigPositive) BigIntConst { | |
| 829 | return BigIntConst{ .limbs = self.limbs, .positive = true }; | |
| 830 | } | |
| 831 | }; | |
| 832 | ||
| 833 | pub const IntBigNegative = struct { | |
| 834 | base: Payload = Payload{ .tag = .int_big_negative }, | |
| 835 | limbs: []const std.math.big.Limb, | |
| 836 | ||
| 837 | pub fn asBigInt(self: IntBigNegative) BigIntConst { | |
| 838 | return BigIntConst{ .limbs = self.limbs, .positive = false }; | |
| 839 | } | |
| 516 | 840 | }; |
| 517 | 841 | |
| 518 | 842 | pub const Function = struct { |
| ... | ... | @@ -550,6 +874,20 @@ pub const Value = extern union { |
| 550 | 874 | base: Payload = Payload{ .tag = .ty }, |
| 551 | 875 | ty: Type, |
| 552 | 876 | }; |
| 877 | ||
| 878 | pub const Repeated = struct { | |
| 879 | base: Payload = Payload{ .tag = .ty }, | |
| 880 | /// This value is repeated some number of times. The amount of times to repeat | |
| 881 | /// is stored externally. | |
| 882 | val: Value, | |
| 883 | }; | |
| 884 | }; | |
| 885 | ||
| 886 | /// Big enough to fit any non-BigInt value | |
| 887 | pub const BigIntSpace = struct { | |
| 888 | /// The +1 is headroom so that operations such as incrementing once or decrementing once | |
| 889 | /// are possible without using an allocator. | |
| 890 | limbs: [(@sizeOf(u64) / @sizeOf(std.math.big.Limb)) + 1]std.math.big.Limb, | |
| 553 | 891 | }; |
| 554 | 892 | }; |
| 555 | 893 |
test/stage2/compare_output.zig+22-19| ... | ... | @@ -2,24 +2,27 @@ const std = @import("std"); |
| 2 | 2 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; |
| 3 | 3 | |
| 4 | 4 | pub fn addCases(ctx: *TestContext) !void { |
| 5 | // hello world | |
| 6 | try ctx.testCompareOutputLibC( | |
| 7 | \\extern fn puts([*]const u8) void; | |
| 8 | \\pub export fn main() c_int { | |
| 9 | \\ puts("Hello, world!"); | |
| 10 | \\ return 0; | |
| 11 | \\} | |
| 12 | , "Hello, world!" ++ std.cstr.line_sep); | |
| 5 | // TODO: re-enable these tests. | |
| 6 | // https://github.com/ziglang/zig/issues/1364 | |
| 13 | 7 | |
| 14 | // function calling another function | |
| 15 | try ctx.testCompareOutputLibC( | |
| 16 | \\extern fn puts(s: [*]const u8) void; | |
| 17 | \\pub export fn main() c_int { | |
| 18 | \\ return foo("OK"); | |
| 19 | \\} | |
| 20 | \\fn foo(s: [*]const u8) c_int { | |
| 21 | \\ puts(s); | |
| 22 | \\ return 0; | |
| 23 | \\} | |
| 24 | , "OK" ++ std.cstr.line_sep); | |
| 8 | //// hello world | |
| 9 | //try ctx.testCompareOutputLibC( | |
| 10 | // \\extern fn puts([*]const u8) void; | |
| 11 | // \\pub export fn main() c_int { | |
| 12 | // \\ puts("Hello, world!"); | |
| 13 | // \\ return 0; | |
| 14 | // \\} | |
| 15 | //, "Hello, world!" ++ std.cstr.line_sep); | |
| 16 | ||
| 17 | //// function calling another function | |
| 18 | //try ctx.testCompareOutputLibC( | |
| 19 | // \\extern fn puts(s: [*]const u8) void; | |
| 20 | // \\pub export fn main() c_int { | |
| 21 | // \\ return foo("OK"); | |
| 22 | // \\} | |
| 23 | // \\fn foo(s: [*]const u8) c_int { | |
| 24 | // \\ puts(s); | |
| 25 | // \\ return 0; | |
| 26 | // \\} | |
| 27 | //, "OK" ++ std.cstr.line_sep); | |
| 25 | 28 | } |
test/stage2/compile_errors.zig+53-50| ... | ... | @@ -1,54 +1,57 @@ |
| 1 | 1 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; |
| 2 | 2 | |
| 3 | 3 | pub fn addCases(ctx: *TestContext) !void { |
| 4 | try ctx.testCompileError( | |
| 5 | \\export fn entry() void {} | |
| 6 | \\export fn entry() void {} | |
| 7 | , "1.zig", 2, 8, "exported symbol collision: 'entry'"); | |
| 8 | ||
| 9 | try ctx.testCompileError( | |
| 10 | \\fn() void {} | |
| 11 | , "1.zig", 1, 1, "missing function name"); | |
| 12 | ||
| 13 | try ctx.testCompileError( | |
| 14 | \\comptime { | |
| 15 | \\ return; | |
| 16 | \\} | |
| 17 | , "1.zig", 2, 5, "return expression outside function definition"); | |
| 18 | ||
| 19 | try ctx.testCompileError( | |
| 20 | \\export fn entry() void { | |
| 21 | \\ defer return; | |
| 22 | \\} | |
| 23 | , "1.zig", 2, 11, "cannot return from defer expression"); | |
| 24 | ||
| 25 | try ctx.testCompileError( | |
| 26 | \\export fn entry() c_int { | |
| 27 | \\ return 36893488147419103232; | |
| 28 | \\} | |
| 29 | , "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'"); | |
| 30 | ||
| 31 | try ctx.testCompileError( | |
| 32 | \\comptime { | |
| 33 | \\ var a: *align(4) align(4) i32 = 0; | |
| 34 | \\} | |
| 35 | , "1.zig", 2, 22, "Extra align qualifier"); | |
| 36 | ||
| 37 | try ctx.testCompileError( | |
| 38 | \\comptime { | |
| 39 | \\ var b: *const const i32 = 0; | |
| 40 | \\} | |
| 41 | , "1.zig", 2, 19, "Extra align qualifier"); | |
| 42 | ||
| 43 | try ctx.testCompileError( | |
| 44 | \\comptime { | |
| 45 | \\ var c: *volatile volatile i32 = 0; | |
| 46 | \\} | |
| 47 | , "1.zig", 2, 22, "Extra align qualifier"); | |
| 48 | ||
| 49 | try ctx.testCompileError( | |
| 50 | \\comptime { | |
| 51 | \\ var d: *allowzero allowzero i32 = 0; | |
| 52 | \\} | |
| 53 | , "1.zig", 2, 23, "Extra align qualifier"); | |
| 4 | // TODO: re-enable these tests. | |
| 5 | // https://github.com/ziglang/zig/issues/1364 | |
| 6 | ||
| 7 | //try ctx.testCompileError( | |
| 8 | // \\export fn entry() void {} | |
| 9 | // \\export fn entry() void {} | |
| 10 | //, "1.zig", 2, 8, "exported symbol collision: 'entry'"); | |
| 11 | ||
| 12 | //try ctx.testCompileError( | |
| 13 | // \\fn() void {} | |
| 14 | //, "1.zig", 1, 1, "missing function name"); | |
| 15 | ||
| 16 | //try ctx.testCompileError( | |
| 17 | // \\comptime { | |
| 18 | // \\ return; | |
| 19 | // \\} | |
| 20 | //, "1.zig", 2, 5, "return expression outside function definition"); | |
| 21 | ||
| 22 | //try ctx.testCompileError( | |
| 23 | // \\export fn entry() void { | |
| 24 | // \\ defer return; | |
| 25 | // \\} | |
| 26 | //, "1.zig", 2, 11, "cannot return from defer expression"); | |
| 27 | ||
| 28 | //try ctx.testCompileError( | |
| 29 | // \\export fn entry() c_int { | |
| 30 | // \\ return 36893488147419103232; | |
| 31 | // \\} | |
| 32 | //, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'"); | |
| 33 | ||
| 34 | //try ctx.testCompileError( | |
| 35 | // \\comptime { | |
| 36 | // \\ var a: *align(4) align(4) i32 = 0; | |
| 37 | // \\} | |
| 38 | //, "1.zig", 2, 22, "Extra align qualifier"); | |
| 39 | ||
| 40 | //try ctx.testCompileError( | |
| 41 | // \\comptime { | |
| 42 | // \\ var b: *const const i32 = 0; | |
| 43 | // \\} | |
| 44 | //, "1.zig", 2, 19, "Extra align qualifier"); | |
| 45 | ||
| 46 | //try ctx.testCompileError( | |
| 47 | // \\comptime { | |
| 48 | // \\ var c: *volatile volatile i32 = 0; | |
| 49 | // \\} | |
| 50 | //, "1.zig", 2, 22, "Extra align qualifier"); | |
| 51 | ||
| 52 | //try ctx.testCompileError( | |
| 53 | // \\comptime { | |
| 54 | // \\ var d: *allowzero allowzero i32 = 0; | |
| 55 | // \\} | |
| 56 | //, "1.zig", 2, 23, "Extra align qualifier"); | |
| 54 | 57 | } |
test/stage2/ir.zig deleted-54| ... | ... | @@ -1,54 +0,0 @@ |
| 1 | test "hello world IR" { | |
| 2 | exeCmp( | |
| 3 | \\@0 = str("Hello, world!\n") | |
| 4 | \\@1 = primitive(void) | |
| 5 | \\@2 = primitive(usize) | |
| 6 | \\@3 = fntype([], @1, cc=Naked) | |
| 7 | \\@4 = int(0) | |
| 8 | \\@5 = int(1) | |
| 9 | \\@6 = int(231) | |
| 10 | \\@7 = str("len") | |
| 11 | \\ | |
| 12 | \\@8 = fn(@3, { | |
| 13 | \\ %0 = as(@2, @5) ; SYS_write | |
| 14 | \\ %1 = as(@2, @5) ; STDOUT_FILENO | |
| 15 | \\ %2 = ptrtoint(@0) ; msg ptr | |
| 16 | \\ %3 = fieldptr(@0, @7) ; msg len ptr | |
| 17 | \\ %4 = deref(%3) ; msg len | |
| 18 | \\ %sysoutreg = str("={rax}") | |
| 19 | \\ %rax = str("{rax}") | |
| 20 | \\ %rdi = str("{rdi}") | |
| 21 | \\ %rsi = str("{rsi}") | |
| 22 | \\ %rdx = str("{rdx}") | |
| 23 | \\ %rcx = str("rcx") | |
| 24 | \\ %r11 = str("r11") | |
| 25 | \\ %memory = str("memory") | |
| 26 | \\ %syscall = str("syscall") | |
| 27 | \\ %5 = asm(%syscall, @2, | |
| 28 | \\ volatile=1, | |
| 29 | \\ output=%sysoutreg, | |
| 30 | \\ inputs=[%rax, %rdi, %rsi, %rdx], | |
| 31 | \\ clobbers=[%rcx, %r11, %memory], | |
| 32 | \\ args=[%0, %1, %2, %4]) | |
| 33 | \\ | |
| 34 | \\ %6 = as(@2, @6) ;SYS_exit_group | |
| 35 | \\ %7 = as(@2, @4) ;exit code | |
| 36 | \\ %8 = asm(%syscall, @2, | |
| 37 | \\ volatile=1, | |
| 38 | \\ output=%sysoutreg, | |
| 39 | \\ inputs=[%rax, %rdi], | |
| 40 | \\ clobbers=[%rcx, %r11, %memory], | |
| 41 | \\ args=[%6, %7]) | |
| 42 | \\ | |
| 43 | \\ %9 = unreachable() | |
| 44 | \\}) | |
| 45 | \\ | |
| 46 | \\@9 = str("_start") | |
| 47 | \\@10 = export(@9, @8) | |
| 48 | , | |
| 49 | \\Hello, world! | |
| 50 | \\ | |
| 51 | ); | |
| 52 | } | |
| 53 | ||
| 54 | fn exeCmp(src: []const u8, expected_stdout: []const u8) void {} |
test/stage2/test.zig+1| ... | ... | @@ -3,4 +3,5 @@ const TestContext = @import("../../src-self-hosted/test.zig").TestContext; |
| 3 | 3 | pub fn addCases(ctx: *TestContext) !void { |
| 4 | 4 | try @import("compile_errors.zig").addCases(ctx); |
| 5 | 5 | try @import("compare_output.zig").addCases(ctx); |
| 6 | @import("zir.zig").addCases(ctx); | |
| 6 | 7 | } |
test/stage2/zir.zig created+107| ... | ... | @@ -0,0 +1,107 @@ |
| 1 | const TestContext = @import("../../src-self-hosted/test.zig").TestContext; | |
| 2 | ||
| 3 | pub fn addCases(ctx: *TestContext) void { | |
| 4 | ctx.addZIRTransform("elemptr, add, cmp, condbr, return, breakpoint", | |
| 5 | \\@void = primitive(void) | |
| 6 | \\@usize = primitive(usize) | |
| 7 | \\@fnty = fntype([], @void, cc=C) | |
| 8 | \\@0 = int(0) | |
| 9 | \\@1 = int(1) | |
| 10 | \\@2 = int(2) | |
| 11 | \\@3 = int(3) | |
| 12 | \\ | |
| 13 | \\@entry = fn(@fnty, { | |
| 14 | \\ %a = str("\x32\x08\x01\x0a") | |
| 15 | \\ %eptr0 = elemptr(%a, @0) | |
| 16 | \\ %eptr1 = elemptr(%a, @1) | |
| 17 | \\ %eptr2 = elemptr(%a, @2) | |
| 18 | \\ %eptr3 = elemptr(%a, @3) | |
| 19 | \\ %v0 = deref(%eptr0) | |
| 20 | \\ %v1 = deref(%eptr1) | |
| 21 | \\ %v2 = deref(%eptr2) | |
| 22 | \\ %v3 = deref(%eptr3) | |
| 23 | \\ %x0 = add(%v0, %v1) | |
| 24 | \\ %x1 = add(%v2, %v3) | |
| 25 | \\ %result = add(%x0, %x1) | |
| 26 | \\ | |
| 27 | \\ %expected = int(69) | |
| 28 | \\ %ok = cmp(%result, eq, %expected) | |
| 29 | \\ %10 = condbr(%ok, { | |
| 30 | \\ %11 = return() | |
| 31 | \\ }, { | |
| 32 | \\ %12 = breakpoint() | |
| 33 | \\ }) | |
| 34 | \\}) | |
| 35 | \\ | |
| 36 | \\@9 = str("entry") | |
| 37 | \\@10 = export(@9, @entry) | |
| 38 | , | |
| 39 | \\@0 = primitive(void) | |
| 40 | \\@1 = fntype([], @0, cc=C) | |
| 41 | \\@2 = fn(@1, { | |
| 42 | \\ %0 = return() | |
| 43 | \\}) | |
| 44 | \\@3 = str("entry") | |
| 45 | \\@4 = export(@3, @2) | |
| 46 | \\ | |
| 47 | ); | |
| 48 | ||
| 49 | if (@import("std").Target.current.os.tag != .linux or | |
| 50 | @import("std").Target.current.cpu.arch != .x86_64) | |
| 51 | { | |
| 52 | // TODO implement self-hosted PE (.exe file) linking | |
| 53 | // TODO implement more ZIR so we don't depend on x86_64-linux | |
| 54 | return; | |
| 55 | } | |
| 56 | ||
| 57 | ctx.addZIRCompareOutput("hello world ZIR", | |
| 58 | \\@0 = str("Hello, world!\n") | |
| 59 | \\@1 = primitive(noreturn) | |
| 60 | \\@2 = primitive(usize) | |
| 61 | \\@3 = fntype([], @1, cc=Naked) | |
| 62 | \\@4 = int(0) | |
| 63 | \\@5 = int(1) | |
| 64 | \\@6 = int(231) | |
| 65 | \\@7 = str("len") | |
| 66 | \\ | |
| 67 | \\@8 = fn(@3, { | |
| 68 | \\ %0 = as(@2, @5) ; SYS_write | |
| 69 | \\ %1 = as(@2, @5) ; STDOUT_FILENO | |
| 70 | \\ %2 = ptrtoint(@0) ; msg ptr | |
| 71 | \\ %3 = fieldptr(@0, @7) ; msg len ptr | |
| 72 | \\ %4 = deref(%3) ; msg len | |
| 73 | \\ %sysoutreg = str("={rax}") | |
| 74 | \\ %rax = str("{rax}") | |
| 75 | \\ %rdi = str("{rdi}") | |
| 76 | \\ %rsi = str("{rsi}") | |
| 77 | \\ %rdx = str("{rdx}") | |
| 78 | \\ %rcx = str("rcx") | |
| 79 | \\ %r11 = str("r11") | |
| 80 | \\ %memory = str("memory") | |
| 81 | \\ %syscall = str("syscall") | |
| 82 | \\ %5 = asm(%syscall, @2, | |
| 83 | \\ volatile=1, | |
| 84 | \\ output=%sysoutreg, | |
| 85 | \\ inputs=[%rax, %rdi, %rsi, %rdx], | |
| 86 | \\ clobbers=[%rcx, %r11, %memory], | |
| 87 | \\ args=[%0, %1, %2, %4]) | |
| 88 | \\ | |
| 89 | \\ %6 = as(@2, @6) ;SYS_exit_group | |
| 90 | \\ %7 = as(@2, @4) ;exit code | |
| 91 | \\ %8 = asm(%syscall, @2, | |
| 92 | \\ volatile=1, | |
| 93 | \\ output=%sysoutreg, | |
| 94 | \\ inputs=[%rax, %rdi], | |
| 95 | \\ clobbers=[%rcx, %r11, %memory], | |
| 96 | \\ args=[%6, %7]) | |
| 97 | \\ | |
| 98 | \\ %9 = unreachable() | |
| 99 | \\}) | |
| 100 | \\ | |
| 101 | \\@9 = str("_start") | |
| 102 | \\@10 = export(@9, @8) | |
| 103 | , | |
| 104 | \\Hello, world! | |
| 105 | \\ | |
| 106 | ); | |
| 107 | } |