| ... | ... | @@ -240,6 +240,19 @@ pub fn Uint(comptime max_bits: comptime_int) type { |
| 240 | 240 | return x.conditionalSubWithOverflow(true, y); |
| 241 | 241 | } |
| 242 | 242 | |
| 243 | /// Adds the product of `x` and `y` to `acc`, wrapping at the active width. |
| 244 | /// Returns 1 if any part of the result was discarded. |
| 245 | pub fn mulAddWithOverflow(acc: *Self, x: Self, y: Self) u1 { |
| 246 | assert(x.limbs_len == acc.limbs_len); |
| 247 | assert(y.limbs_len == acc.limbs_len); |
| 248 | const n = acc.limbs_len; |
| 249 | var wide: [2 * max_limbs_count]Limb = undefined; |
| 250 | @memcpy(wide[0..n], acc.limbsConst()); |
| 251 | addMulVV(wide[0 .. 2 * n], x.limbsConst(), y.limbsConst()); |
| 252 | @memcpy(acc.limbs(), wide[0..n]); |
| 253 | return @intFromBool(!ct.eql(orLimbs(wide[n..][0..n]), 0)); |
| 254 | } |
| 255 | |
| 243 | 256 | fn expandTo(x: *Self, new_len: usize) void { |
| 244 | 257 | assert(new_len >= x.limbs_len and new_len <= x.limbs_buffer.len); |
| 245 | 258 | @memset(x.limbs_buffer[x.limbs_len..new_len], 0); |
| ... | ... | @@ -402,6 +415,30 @@ fn orLimbs(limbs: []const Limb) Limb { |
| 402 | 415 | return t; |
| 403 | 416 | } |
| 404 | 417 | |
| 418 | // Adds `x * y` to `z` and returns the carry. |
| 419 | fn addMulVVW(z: []Limb, x: []const Limb, y: Limb) Limb { |
| 420 | assert(z.len == x.len); |
| 421 | var carry: Limb = 0; |
| 422 | for (z, x) |*z_limb, x_limb| { |
| 423 | const wide = ct.mulWide(x_limb, y); |
| 424 | var z_lo = @addWithOverflow(z_limb.*, wide.lo); |
| 425 | var z_hi = wide.hi +% z_lo[1]; |
| 426 | z_lo = @addWithOverflow(z_lo[0], carry); |
| 427 | z_hi +%= z_lo[1]; |
| 428 | z_limb.* = @as(TLimb, @truncate(z_lo[0])); |
| 429 | carry = (z_hi << 1) | (z_lo[0] >> t_bits); |
| 430 | } |
| 431 | return carry; |
| 432 | } |
| 433 | |
| 434 | // Adds `x * y` to the low `x.len` limbs of `z`. The upper limbs need no initialization. |
| 435 | fn addMulVV(z: []Limb, x: []const Limb, y: []const Limb) void { |
| 436 | assert(z.len == x.len + y.len); |
| 437 | for (y, 0..) |y_limb, i| { |
| 438 | z[i + x.len] = addMulVVW(z[i..][0..x.len], x, y_limb); |
| 439 | } |
| 440 | } |
| 441 | |
| 405 | 442 | /// A modulus, defining a finite field. |
| 406 | 443 | /// All operations within the field are performed modulo this modulus, without heap allocations. |
| 407 | 444 | /// `max_bits` represents the number of bits in the maximum value the modulus can be set to. |
| ... | ... | @@ -1236,4 +1273,29 @@ test "Uint addition and multiply-add" { |
| 1236 | 1273 | try testing.expect(wrapped.eql(full)); |
| 1237 | 1274 | try testing.expectEqual(1, wrapped.addWithOverflow(one)); |
| 1238 | 1275 | try testing.expect(wrapped.isZero()); |
| 1276 | |
| 1277 | for ([_]struct { acc: U, x: U, y: U, overflow: u1, expected: U }{ |
| 1278 | .{ .acc = U.zero, .x = full, .y = one, .overflow = 0, .expected = full }, |
| 1279 | .{ .acc = full, .x = U.zero, .y = full, .overflow = 0, .expected = full }, |
| 1280 | .{ .acc = full, .x = full, .y = U.zero, .overflow = 0, .expected = full }, |
| 1281 | .{ .acc = U.zero, .x = full, .y = full, .overflow = 1, .expected = one }, |
| 1282 | .{ .acc = full, .x = one, .y = one, .overflow = 1, .expected = U.zero }, |
| 1283 | }) |c| { |
| 1284 | var acc = c.acc; |
| 1285 | try testing.expectEqual(c.overflow, acc.mulAddWithOverflow(c.x, c.y)); |
| 1286 | try testing.expect(acc.eql(c.expected)); |
| 1287 | try expectWellFormedLimbs(acc); |
| 1288 | } |
| 1289 | const a: u256 = (1 << 100) + 3; |
| 1290 | const b: u256 = (1 << 80) + 7; |
| 1291 | var acc = try U.fromPrimitive(u256, a); |
| 1292 | try testing.expectEqual(0, acc.mulAddWithOverflow(try U.fromPrimitive(u256, a), try U.fromPrimitive(u256, b))); |
| 1293 | try testing.expectEqual(a + a * b, try acc.toPrimitive(u256)); |
| 1294 | |
| 1295 | var short = one.normalize(); |
| 1296 | const high = (try U.fromPrimitive(u64, 1 << (t_bits - 1))).normalize(); |
| 1297 | const two = (try U.fromPrimitive(u8, 2)).normalize(); |
| 1298 | try testing.expectEqual(1, short.mulAddWithOverflow(high, two)); |
| 1299 | try testing.expectEqual(1, short.limbs_len); |
| 1300 | try testing.expect(short.isOne()); |
| 1239 | 1301 | } |