| ... | ... | @@ -86,6 +86,15 @@ pub fn addMulLimbWithCarry(a: Limb, b: Limb, c: Limb, carry: *Limb) Limb { |
| 86 | 86 | return r1; |
| 87 | 87 | } |
| 88 | 88 | |
| 89 | /// Used to indicate either limit of a 2s-complement integer. |
| 90 | pub const TwosCompIntLimit = enum { |
| 91 | // The low limit, either 0x00 (unsigned) or (-)0x80 (signed) for an 8-bit integer. |
| 92 | min, |
| 93 | |
| 94 | // The high limit, either 0xFF (unsigned) or 0x7F (signed) for an 8-bit integer. |
| 95 | max, |
| 96 | }; |
| 97 | |
| 89 | 98 | /// A arbitrary-precision big integer, with a fixed set of mutable limbs. |
| 90 | 99 | pub const Mutable = struct { |
| 91 | 100 | /// Raw digits. These are: |
| ... | ... | @@ -287,6 +296,75 @@ pub const Mutable = struct { |
| 287 | 296 | self.positive = positive; |
| 288 | 297 | } |
| 289 | 298 | |
| 299 | /// Set self to either bound of a 2s-complement integer. |
| 300 | /// Note: The result is still sign-magnitude, not twos complement! In order to convert the |
| 301 | /// result to twos complement, it is sufficient to take the absolute value. |
| 302 | /// |
| 303 | /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by |
| 304 | /// r is `calcTwosCompLimbCount(bit_count)`. |
| 305 | pub fn setTwosCompIntLimit( |
| 306 | r: *Mutable, |
| 307 | limit: TwosCompIntLimit, |
| 308 | signedness: std.builtin.Signedness, |
| 309 | bit_count: usize |
| 310 | ) void { |
| 311 | // Handle zero-bit types. |
| 312 | if (bit_count == 0) { |
| 313 | r.set(0); |
| 314 | return; |
| 315 | } |
| 316 | |
| 317 | const req_limbs = calcTwosCompLimbCount(bit_count); |
| 318 | const bit = @truncate(Log2Limb, bit_count - 1); |
| 319 | const signmask = @as(Limb, 1) << bit; // 0b0..010..0 where 1 is the sign bit. |
| 320 | const mask = (signmask << 1) -% 1; // 0b0..011..1 where the leftmost 1 is the sign bit. |
| 321 | |
| 322 | r.positive = true; |
| 323 | |
| 324 | switch (signedness) { |
| 325 | .signed => switch (limit) { |
| 326 | .min => { |
| 327 | // Negative bound, signed = -0x80. |
| 328 | r.len = req_limbs; |
| 329 | mem.set(Limb, r.limbs[0..r.len - 1], 0); |
| 330 | r.limbs[r.len - 1] = signmask; |
| 331 | r.positive = false; |
| 332 | }, |
| 333 | .max => { |
| 334 | // Positive bound, signed = 0x7F |
| 335 | // Note, in this branch we need to normalize because the first bit is |
| 336 | // supposed to be 0. |
| 337 | |
| 338 | // Special case for 1-bit integers. |
| 339 | if (bit_count == 1) { |
| 340 | r.set(0); |
| 341 | } else { |
| 342 | const new_req_limbs = calcTwosCompLimbCount(bit_count - 1); |
| 343 | const msb = @truncate(Log2Limb, bit_count - 2); |
| 344 | const new_signmask = @as(Limb, 1) << msb; // 0b0..010..0 where 1 is the sign bit. |
| 345 | const new_mask = (new_signmask << 1) -% 1; // 0b0..001..1 where the rightmost 0 is the sign bit. |
| 346 | |
| 347 | r.len = new_req_limbs; |
| 348 | std.mem.set(Limb, r.limbs[0..r.len - 1], maxInt(Limb)); |
| 349 | r.limbs[r.len - 1] = new_mask; |
| 350 | } |
| 351 | }, |
| 352 | }, |
| 353 | .unsigned => switch (limit) { |
| 354 | .min => { |
| 355 | // Min bound, unsigned = 0x00 |
| 356 | r.set(0); |
| 357 | }, |
| 358 | .max => { |
| 359 | // Max bound, unsigned = 0xFF |
| 360 | r.len = req_limbs; |
| 361 | std.mem.set(Limb, r.limbs[0..r.len - 1], maxInt(Limb)); |
| 362 | r.limbs[r.len - 1] = mask; |
| 363 | }, |
| 364 | }, |
| 365 | } |
| 366 | } |
| 367 | |
| 290 | 368 | /// r = a + scalar |
| 291 | 369 | /// |
| 292 | 370 | /// r and a may be aliases. |
| ... | ... | @@ -335,7 +413,6 @@ pub const Mutable = struct { |
| 335 | 413 | } |
| 336 | 414 | |
| 337 | 415 | /// r = a + b |
| 338 | | /// |
| 339 | 416 | /// r, a and b may be aliases. |
| 340 | 417 | /// |
| 341 | 418 | /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by |
| ... | ... | @@ -353,8 +430,8 @@ pub const Mutable = struct { |
| 353 | 430 | } |
| 354 | 431 | |
| 355 | 432 | /// r = a + b with 2s-complement wrapping semantics. |
| 356 | | /// |
| 357 | 433 | /// r, a and b may be aliases |
| 434 | /// |
| 358 | 435 | /// Asserts the result fits in `r`. An upper bound on the number of limbs needed by |
| 359 | 436 | /// r is `calcTwosCompLimbCount(bit_count)`. |
| 360 | 437 | pub fn addWrap(r: *Mutable, a: Const, b: Const, signedness: std.builtin.Signedness, bit_count: usize) void { |
| ... | ... | @@ -374,7 +451,8 @@ pub const Mutable = struct { |
| 374 | 451 | |
| 375 | 452 | if (r.addCarry(x, y)) { |
| 376 | 453 | // There are two possibilities here: |
| 377 | | // - We overflowed req_limbs. In this case, the carry is ignored. |
| 454 | // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by |
| 455 | // truncate anyway. |
| 378 | 456 | // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled. |
| 379 | 457 | const msl = math.max(a.limbs.len, b.limbs.len); |
| 380 | 458 | if (msl < req_limbs) { |
| ... | ... | @@ -399,7 +477,7 @@ pub const Mutable = struct { |
| 399 | 477 | } else if (b.eqZero()) { |
| 400 | 478 | r.copy(a); |
| 401 | 479 | return false; |
| 402 | | } if (a.positive != b.positive) { |
| 480 | } else if (a.positive != b.positive) { |
| 403 | 481 | if (a.positive) { |
| 404 | 482 | // (a) - (-b) => a + b |
| 405 | 483 | return r.addCarry(a, b.abs()); |
| ... | ... | @@ -478,7 +556,8 @@ pub const Mutable = struct { |
| 478 | 556 | |
| 479 | 557 | if (r.subCarry(x, y)) { |
| 480 | 558 | // There are two possibilities here: |
| 481 | | // - We overflowed req_limbs. In this case, the carry is ignored. |
| 559 | // - We overflowed req_limbs. In this case, the carry is ignored, as it would be removed by |
| 560 | // truncate anyway. |
| 482 | 561 | // - a and b had less elements than req_limbs, and those were overflowed. This case needs to be handled. |
| 483 | 562 | const msl = math.max(a.limbs.len, b.limbs.len); |
| 484 | 563 | if (msl < req_limbs) { |
| ... | ... | @@ -1131,8 +1210,8 @@ pub const Mutable = struct { |
| 1131 | 1210 | } |
| 1132 | 1211 | |
| 1133 | 1212 | const bit = @truncate(Log2Limb, bit_count - 1); |
| 1134 | | const signmask = @as(Limb, 1) << bit; |
| 1135 | | const mask = (signmask << 1) -% 1; |
| 1213 | const signmask = @as(Limb, 1) << bit; // 0b0..010...0 where 1 is the sign bit. |
| 1214 | const mask = (signmask << 1) -% 1; // 0b0..01..1 where the leftmost 1 is the sign bit. |
| 1136 | 1215 | |
| 1137 | 1216 | if (!a.positive) { |
| 1138 | 1217 | // Convert the integer from sign-magnitude into twos-complement. |
| ... | ... | @@ -1871,6 +1950,21 @@ pub const Managed = struct { |
| 1871 | 1950 | self.setMetadata(m.positive, m.len); |
| 1872 | 1951 | } |
| 1873 | 1952 | |
| 1953 | /// Set self to either bound of a 2s-complement integer. |
| 1954 | /// Note: The result is still sign-magnitude, not twos complement! In order to convert the |
| 1955 | /// result to twos complement, it is sufficient to take the absolute value. |
| 1956 | pub fn setTwosCompIntLimit( |
| 1957 | r: *Managed, |
| 1958 | limit: TwosCompIntLimit, |
| 1959 | signedness: std.builtin.Signedness, |
| 1960 | bit_count: usize |
| 1961 | ) !void { |
| 1962 | try r.ensureCapacity(calcTwosCompLimbCount(bit_count)); |
| 1963 | var m = r.toMutable(); |
| 1964 | m.setTwosCompIntLimit(limit, signedness, bit_count); |
| 1965 | r.setMetadata(m.positive, m.len); |
| 1966 | } |
| 1967 | |
| 1874 | 1968 | /// Converts self to a string in the requested base. Memory is allocated from the provided |
| 1875 | 1969 | /// allocator and not the one present in self. |
| 1876 | 1970 | pub fn toString(self: Managed, allocator: *Allocator, base: u8, case: std.fmt.Case) ![]u8 { |
| ... | ... | @@ -2184,7 +2278,7 @@ pub const Managed = struct { |
| 2184 | 2278 | } |
| 2185 | 2279 | } |
| 2186 | 2280 | |
| 2187 | | // r = truncate(Int(signedness, bit_count), a) |
| 2281 | /// r = truncate(Int(signedness, bit_count), a) |
| 2188 | 2282 | pub fn truncate(r: *Managed, a: Const, signedness: std.builtin.Signedness, bit_count: usize) !void { |
| 2189 | 2283 | try r.ensureCapacity(calcTwosCompLimbCount(bit_count)); |
| 2190 | 2284 | var m = r.toMutable(); |