| ... | @@ -240,6 +240,19 @@ pub fn Uint(comptime max_bits: comptime_int) type { | ... | @@ -240,6 +240,19 @@ pub fn Uint(comptime max_bits: comptime_int) type { |
| 240 | return x.conditionalSubWithOverflow(true, y); | 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 | fn expandTo(x: *Self, new_len: usize) void { | 256 | fn expandTo(x: *Self, new_len: usize) void { |
| 244 | assert(new_len >= x.limbs_len and new_len <= x.limbs_buffer.len); | 257 | assert(new_len >= x.limbs_len and new_len <= x.limbs_buffer.len); |
| 245 | @memset(x.limbs_buffer[x.limbs_len..new_len], 0); | 258 | @memset(x.limbs_buffer[x.limbs_len..new_len], 0); |
| ... | @@ -402,6 +415,30 @@ fn orLimbs(limbs: []const Limb) Limb { | ... | @@ -402,6 +415,30 @@ fn orLimbs(limbs: []const Limb) Limb { |
| 402 | return t; | 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 | /// A modulus, defining a finite field. | 442 | /// A modulus, defining a finite field. |
| 406 | /// All operations within the field are performed modulo this modulus, without heap allocations. | 443 | /// All operations within the field are performed modulo this modulus, without heap allocations. |
| 407 | /// `max_bits` represents the number of bits in the maximum value the modulus can be set to. | 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,4 +1273,29 @@ test "Uint addition and multiply-add" { |
| 1236 | try testing.expect(wrapped.eql(full)); | 1273 | try testing.expect(wrapped.eql(full)); |
| 1237 | try testing.expectEqual(1, wrapped.addWithOverflow(one)); | 1274 | try testing.expectEqual(1, wrapped.addWithOverflow(one)); |
| 1238 | try testing.expect(wrapped.isZero()); | 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 | } |