authorgravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-23 21:03:19+02:00
committergravatar for robin@voetter.nlRobin Voetter <robin@voetter.nl> 2021-10-24 01:21:33+02:00
log87b7b31557b182411f22b613aec7cbf7396972fe
treede43b88b2d88762a0d66b7e03ff13331f02c2089
parentee98d8700818aa667137e3aa580b16df2ba6d680

big ints: improve division


3 files changed, 151 insertions(+), 112 deletions(-)

lib/std/math/big/int.zig+137-98
...@@ -33,7 +33,7 @@ pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {...@@ -33,7 +33,7 @@ pub fn calcToStringLimbsBufferLen(a_len: usize, base: u8) usize {
33}33}
3434
35pub fn calcDivLimbsBufferLen(a_len: usize, b_len: usize) usize {35pub fn calcDivLimbsBufferLen(a_len: usize, b_len: usize) usize {
36 return calcMulLimbsBufferLen(a_len, b_len, 2) * 4;36 return a_len + b_len + 4;
37}37}
3838
39pub fn calcMulLimbsBufferLen(a_len: usize, b_len: usize, aliases: usize) usize {39pub fn calcMulLimbsBufferLen(a_len: usize, b_len: usize, aliases: usize) usize {
...@@ -760,8 +760,8 @@ pub const Mutable = struct {...@@ -760,8 +760,8 @@ pub const Mutable = struct {
760 /// q may alias with a or b.760 /// q may alias with a or b.
761 ///761 ///
762 /// Asserts there is enough memory to store q and r.762 /// Asserts there is enough memory to store q and r.
763 /// The upper bound for r limb count is a.limbs.len.763 /// The upper bound for r limb count is b.limbs.len.
764 /// The upper bound for q limb count is given by `a.limbs.len + b.limbs.len + 1`.764 /// The upper bound for q limb count is given by `a.limbs.len + b.limbs.len`.
765 ///765 ///
766 /// If `allocator` is provided, it will be used for temporary storage to improve766 /// If `allocator` is provided, it will be used for temporary storage to improve
767 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.767 /// multiplication performance. `error.OutOfMemory` is handled with a fallback algorithm.
...@@ -773,19 +773,17 @@ pub const Mutable = struct {...@@ -773,19 +773,17 @@ pub const Mutable = struct {
773 a: Const,773 a: Const,
774 b: Const,774 b: Const,
775 limbs_buffer: []Limb,775 limbs_buffer: []Limb,
776 allocator: ?*Allocator,
777 ) void {776 ) void {
778 div(q, r, a, b, limbs_buffer, allocator);777 div(q, r, a, b, limbs_buffer);
779778
780 // Trunc -> Floor.779 // Trunc -> Floor.
781 if (a.positive and b.positive) return;780 if (a.positive and b.positive) return;
782781
783 if ((!q.positive or q.eqZero()) and !r.eqZero()) {782 if ((!q.positive or q.eqZero()) and !r.eqZero()) {
784 const one: Const = .{ .limbs = &[_]Limb{1}, .positive = true };783 q.addScalar(q.toConst(), -1);
785 q.sub(q.toConst(), one);
786 }784 }
787785
788 r.mulNoAlias(q.toConst(), b, allocator);786 r.mulNoAlias(q.toConst(), b, null);
789 r.sub(a, r.toConst());787 r.sub(a, r.toConst());
790 }788 }
791789
...@@ -809,9 +807,8 @@ pub const Mutable = struct {...@@ -809,9 +807,8 @@ pub const Mutable = struct {
809 a: Const,807 a: Const,
810 b: Const,808 b: Const,
811 limbs_buffer: []Limb,809 limbs_buffer: []Limb,
812 allocator: ?*Allocator,
813 ) void {810 ) void {
814 div(q, r, a, b, limbs_buffer, allocator);811 div(q, r, a, b, limbs_buffer);
815 r.positive = a.positive;812 r.positive = a.positive;
816 }813 }
817814
...@@ -1177,7 +1174,7 @@ pub const Mutable = struct {...@@ -1177,7 +1174,7 @@ pub const Mutable = struct {
1177 }1174 }
11781175
1179 /// Truncates by default.1176 /// Truncates by default.
1180 fn div(quo: *Mutable, rem: *Mutable, a: Const, b: Const, limbs_buffer: []Limb, allocator: ?*Allocator) void {1177 fn div(quo: *Mutable, rem: *Mutable, a: Const, b: Const, limbs_buffer: []Limb) void {
1181 assert(!b.eqZero()); // division by zero1178 assert(!b.eqZero()); // division by zero
1182 assert(quo != rem); // illegal aliasing1179 assert(quo != rem); // illegal aliasing
11831180
...@@ -1220,11 +1217,9 @@ pub const Mutable = struct {...@@ -1220,11 +1217,9 @@ pub const Mutable = struct {
1220 rem.positive = true;1217 rem.positive = true;
1221 } else {1218 } else {
1222 // x and y are modified during division1219 // x and y are modified during division
1223 const sep_len = calcMulLimbsBufferLen(a.limbs.len, b.limbs.len, 2);1220 const sep_len = a.limbs.len + 2;
1224 const x_limbs = limbs_buffer[0 * sep_len ..][0..sep_len];1221 const x_limbs = limbs_buffer[0 .. sep_len];
1225 const y_limbs = limbs_buffer[1 * sep_len ..][0..sep_len];1222 const y_limbs = limbs_buffer[sep_len..];
1226 const t_limbs = limbs_buffer[2 * sep_len ..][0..sep_len];
1227 const mul_limbs_buf = limbs_buffer[3 * sep_len ..][0..sep_len];
12281223
1229 var x: Mutable = .{1224 var x: Mutable = .{
1230 .limbs = x_limbs,1225 .limbs = x_limbs,
...@@ -1238,119 +1233,159 @@ pub const Mutable = struct {...@@ -1238,119 +1233,159 @@ pub const Mutable = struct {
1238 };1233 };
12391234
1240 // Shrink x, y such that the trailing zero limbs shared between are removed.1235 // Shrink x, y such that the trailing zero limbs shared between are removed.
1241 mem.copy(Limb, x.limbs, a.limbs[ab_zero_limb_count..a.limbs.len]);1236 mem.copy(Limb, x.limbs, a.limbs[ab_zero_limb_count..]);
1242 mem.copy(Limb, y.limbs, b.limbs[ab_zero_limb_count..b.limbs.len]);1237 mem.copy(Limb, y.limbs, b.limbs[ab_zero_limb_count..]);
12431238
1244 divN(quo, rem, &x, &y, t_limbs, mul_limbs_buf, allocator);1239 divmod(quo, rem, &x, &y);
1245 quo.positive = (a.positive == b.positive);1240 quo.positive = (a.positive == b.positive);
1246 }1241 }
12471242
1248 if (ab_zero_limb_count != 0) {1243 if (ab_zero_limb_count != 0) {
1249 rem.shiftLeft(rem.toConst(), ab_zero_limb_count * limb_bits);1244 // Manually shift here since we know its limb aligned.
1245 mem.copyBackwards(Limb, rem.limbs[ab_zero_limb_count..], rem.limbs[0..rem.len]);
1246 mem.set(Limb, rem.limbs[0..ab_zero_limb_count], 0);
1247 rem.len += ab_zero_limb_count;
1250 }1248 }
1251 }1249 }
12521250
1253 /// Handbook of Applied Cryptography, 14.201251 /// Handbook of Applied Cryptography, 14.20
1254 ///1252 ///
1255 /// x = qy + r where 0 <= r < y1253 /// x = qy + r where 0 <= r < y
1256 fn divN(1254 fn divmod(
1257 q: *Mutable,1255 q: *Mutable,
1258 r: *Mutable,1256 r: *Mutable,
1259 x: *Mutable,1257 x: *Mutable,
1260 y: *Mutable,1258 y: *Mutable,
1261 tmp_limbs: []Limb,
1262 mul_limb_buf: []Limb,
1263 allocator: ?*Allocator,
1264 ) void {1259 ) void {
1265 assert(y.len >= 2);1260 // 0.
1266 assert(x.len >= y.len);1261 // Normalize so that y[t] > b/2
1267 assert(q.limbs.len >= x.len + y.len - 1);1262 const lz = @clz(Limb, y.limbs[y.len - 1]);
12681263 const norm_shift = if (lz == 0 and y.toConst().isOdd())
1269 // See 3.21264 limb_bits // Force an extra limb so that y is even.
1270 var backup_tmp_limbs: [3]Limb = undefined;1265 else
1271 const t_limbs = if (tmp_limbs.len < 3) &backup_tmp_limbs else tmp_limbs;1266 lz;
1272
1273 var tmp: Mutable = .{
1274 .limbs = t_limbs,
1275 .len = 1,
1276 .positive = true,
1277 };
1278 tmp.limbs[0] = 0;
12791267
1280 // Normalize so y > limb_bits / 2 (i.e. leading bit is set) and even
1281 var norm_shift = @clz(Limb, y.limbs[y.len - 1]);
1282 if (norm_shift == 0 and y.toConst().isOdd()) {
1283 norm_shift = limb_bits;
1284 }
1285 x.shiftLeft(x.toConst(), norm_shift);1268 x.shiftLeft(x.toConst(), norm_shift);
1286 y.shiftLeft(y.toConst(), norm_shift);1269 y.shiftLeft(y.toConst(), norm_shift);
12871270
1288 const n = x.len - 1;1271 const n = x.len - 1;
1289 const t = y.len - 1;1272 const t = y.len - 1;
1273 const shift = n - t;
12901274
1291 // 1.1275 // 1.
1292 q.len = n - t + 1;1276 // for 0 <= j <= n - t, set q[j] to 0
1277 q.len = shift + 1;
1293 q.positive = true;1278 q.positive = true;
1294 mem.set(Limb, q.limbs[0..q.len], 0);1279 mem.set(Limb, q.limbs[0..q.len], 0);
12951280
1296 // 2.1281 // 2.
1297 tmp.shiftLeft(y.toConst(), limb_bits * (n - t));1282 // while x >= y * b^(n - t):
1298 while (x.toConst().order(tmp.toConst()) != .lt) {1283 // x -= y * b^(n - t)
1299 q.limbs[n - t] += 1;1284 // q[n - t] += 1
1300 x.sub(x.toConst(), tmp.toConst());1285 // Note, this algorithm is performed only once if y[t] > radix/2 and y is even, which we
1286 // enforced in step 0. This means we can replace the while with an if.
1287 // Note, multiplication by b^(n - t) comes down to shifting to the right by n - t limbs.
1288 // We can also replace x >= y * b^(n - t) by x/b^(n - t) >= y, and use shifts for that.
1289 {
1290 // x >= y * b^(n - t) can be replaced by x/b^(n - t) >= y.
1291
1292 // 'divide' x by b^(n - t)
1293 var tmp = Mutable{
1294 .limbs = x.limbs[shift..],
1295 .len = x.len - shift,
1296 .positive = true,
1297 };
1298
1299 if (tmp.toConst().order(y.toConst()) != .lt) {
1300 // Perform x -= y * b^(n - t)
1301 // Note, we can subtract y from x[n - t..] and get the result without shifting.
1302 // We can also re-use tmp which already contains the relevant part of x. Note that
1303 // this also edits x.
1304 // Due to the check above, this cannot underflow.
1305 tmp.sub(tmp.toConst(), y.toConst());
1306
1307 // tmp.sub normalized tmp, but we need to normalize x now.
1308 x.limbs.len = tmp.limbs.len + shift;
1309
1310 q.limbs[shift] += 1;
1311 }
1301 }1312 }
13021313
1303 // 3.1314 // 3.
1315 // for i from n down to t + 1, do
1304 var i = n;1316 var i = n;
1305 while (i > t) : (i -= 1) {1317 while (i >= t + 1) : (i -= 1) {
1306 // 3.11318 const k = i - t - 1;
1319 // 3.1.
1320 // if x_i == y_t:
1321 // q[i - t - 1] = b - 1
1322 // else:
1323 // q[i - t - 1] = (x[i] * b + x[i - 1]) / y[t]
1307 if (x.limbs[i] == y.limbs[t]) {1324 if (x.limbs[i] == y.limbs[t]) {
1308 q.limbs[i - t - 1] = maxInt(Limb);1325 q.limbs[k] = maxInt(Limb);
1309 } else {1326 } else {
1310 const num = (@as(DoubleLimb, x.limbs[i]) << limb_bits) | @as(DoubleLimb, x.limbs[i - 1]);1327 const q0 = (@as(DoubleLimb, x.limbs[i]) << limb_bits) | @as(DoubleLimb, x.limbs[i - 1]);
1311 const z = @intCast(Limb, num / @as(DoubleLimb, y.limbs[t]));1328 const n0 = @as(DoubleLimb, y.limbs[t]);
1312 q.limbs[i - t - 1] = if (z > maxInt(Limb)) maxInt(Limb) else @as(Limb, z);1329 q.limbs[k] = @intCast(Limb, q0 / n0);
1313 }1330 }
13141331
1315 // 3.21332 // 3.2
1316 tmp.limbs[0] = if (i >= 2) x.limbs[i - 2] else 0;1333 // while q[i - t - 1] * (y[t] * b + y[t - 1] > x[i] * b * b + x[i - 1] + x[i - 2]:
1317 tmp.limbs[1] = if (i >= 1) x.limbs[i - 1] else 0;1334 // q[i - t - 1] -= 1
1318 tmp.limbs[2] = x.limbs[i];1335 // Note, if y[t] > b / 2 this part is repeated no more than twice.
1319 tmp.normalize(3);1336
1337 // Extract from y.
1338 const y0 = if (t > 0) y.limbs[t - 1] else 0;
1339 const y1 = y.limbs[t];
1340
1341 // Extract from x.
1342 // Note, big endian.
1343 const tmp0 = [_]Limb{
1344 x.limbs[i],
1345 if (i >= 1) x.limbs[i - 1] else 0,
1346 if (i >= 2) x.limbs[i - 2] else 0,
1347 };
13201348
1321 while (true) {1349 while (true) {
1322 // 2x1 limb multiplication unrolled against single-limb q[i-t-1]1350 // Ad-hoc 2x1 multiplication with q[i - t - 1].
1323 var carry: Limb = 0;1351 // Note, big endian.
1324 r.limbs[0] = addMulLimbWithCarry(0, if (t >= 1) y.limbs[t - 1] else 0, q.limbs[i - t - 1], &carry);1352 var tmp1 = [_]Limb{0, undefined, undefined};
1325 r.limbs[1] = addMulLimbWithCarry(0, y.limbs[t], q.limbs[i - t - 1], &carry);1353 tmp1[2] = addMulLimbWithCarry(0, y0, q.limbs[k], &tmp1[0]);
1326 r.limbs[2] = carry;1354 tmp1[1] = addMulLimbWithCarry(0, y1, q.limbs[k], &tmp1[0]);
1327 r.normalize(3);1355
13281356 // Big-endian compare
1329 if (r.toConst().orderAbs(tmp.toConst()) != .gt) {1357 if (mem.order(Limb, &tmp1, &tmp0) != .gt)
1330 break;1358 break;
1331 }
13321359
1333 q.limbs[i - t - 1] -= 1;1360 q.limbs[k] -= 1;
1334 }1361 }
13351362
1336 // 3.31363 // 3.3.
1337 tmp.set(q.limbs[i - t - 1]);1364 // x -= q[i - t - 1] * y * b^(i - t - 1)
1338 tmp.mul(tmp.toConst(), y.toConst(), mul_limb_buf, allocator);1365 // Note, we multiply by a single limb here.
1339 tmp.shiftLeft(tmp.toConst(), limb_bits * (i - t - 1));1366 // The shift doesn't need to be performed if we add the result of the first multiplication
1340 x.sub(x.toConst(), tmp.toConst());1367 // to x[i - t - 1].
13411368 // mem.set(Limb, x.limbs, 0);
1342 if (!x.positive) {1369 const underflow = llmulLimb(.sub, x.limbs[k .. x.len], y.limbs[0 .. y.len], q.limbs[k]);
1343 tmp.shiftLeft(y.toConst(), limb_bits * (i - t - 1));1370
1344 x.add(x.toConst(), tmp.toConst());1371 // 3.4.
1345 q.limbs[i - t - 1] -= 1;1372 // if x < 0:
1373 // x += y * b^(i - t - 1)
1374 // q[i - t - 1] -= 1
1375 // Note, we check for x < 0 using the underflow flag from the previous operation.
1376 if (underflow) {
1377 // While we didn't properly set the signedness of x, this operation should 'flow' it back to positive.
1378 llaccum(.add, x.limbs[k .. x.len], y.limbs[0 .. y.len]);
1379 q.limbs[k] -= 1;
1346 }1380 }
1381
1382 x.normalize(x.len);
1347 }1383 }
13481384
1349 // Denormalize
1350 q.normalize(q.len);1385 q.normalize(q.len);
13511386
1387 // De-normalize r.
1352 r.shiftRight(x.toConst(), norm_shift);1388 r.shiftRight(x.toConst(), norm_shift);
1353 r.normalize(r.len);
1354 }1389 }
13551390
1356 /// Truncate an integer to a number of bits, following 2s-complement semantics.1391 /// Truncate an integer to a number of bits, following 2s-complement semantics.
...@@ -1808,7 +1843,7 @@ pub const Const = struct {...@@ -1808,7 +1843,7 @@ pub const Const = struct {
1808 while (q.len >= 2) {1843 while (q.len >= 2) {
1809 // Passing an allocator here would not be helpful since this division is destroying1844 // Passing an allocator here would not be helpful since this division is destroying
1810 // information, not creating it. [TODO citation needed]1845 // information, not creating it. [TODO citation needed]
1811 q.divTrunc(&r, q.toConst(), b, rest_of_the_limbs_buf, null);1846 q.divTrunc(&r, q.toConst(), b, rest_of_the_limbs_buf);
18121847
1813 var r_word = r.limbs[0];1848 var r_word = r.limbs[0];
1814 var i: usize = 0;1849 var i: usize = 0;
...@@ -2435,16 +2470,14 @@ pub const Managed = struct {...@@ -2435,16 +2470,14 @@ pub const Managed = struct {
2435 /// a / b are floored (rounded towards 0).2470 /// a / b are floored (rounded towards 0).
2436 ///2471 ///
2437 /// Returns an error if memory could not be allocated.2472 /// Returns an error if memory could not be allocated.
2438 ///
2439 /// q's allocator is used for temporary storage to speed up the multiplication.
2440 pub fn divFloor(q: *Managed, r: *Managed, a: Const, b: Const) !void {2473 pub fn divFloor(q: *Managed, r: *Managed, a: Const, b: Const) !void {
2441 try q.ensureCapacity(a.limbs.len + b.limbs.len + 1);2474 try q.ensureCapacity(a.limbs.len + b.limbs.len);
2442 try r.ensureCapacity(a.limbs.len);2475 try r.ensureCapacity(b.limbs.len);
2443 var mq = q.toMutable();2476 var mq = q.toMutable();
2444 var mr = r.toMutable();2477 var mr = r.toMutable();
2445 const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.limbs.len, b.limbs.len));2478 const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.limbs.len, b.limbs.len));
2446 defer q.allocator.free(limbs_buffer);2479 defer q.allocator.free(limbs_buffer);
2447 mq.divFloor(&mr, a, b, limbs_buffer, q.allocator);2480 mq.divFloor(&mr, a, b, limbs_buffer);
2448 q.setMetadata(mq.positive, mq.len);2481 q.setMetadata(mq.positive, mq.len);
2449 r.setMetadata(mr.positive, mr.len);2482 r.setMetadata(mr.positive, mr.len);
2450 }2483 }
...@@ -2454,16 +2487,14 @@ pub const Managed = struct {...@@ -2454,16 +2487,14 @@ pub const Managed = struct {
2454 /// a / b are truncated (rounded towards -inf).2487 /// a / b are truncated (rounded towards -inf).
2455 ///2488 ///
2456 /// Returns an error if memory could not be allocated.2489 /// Returns an error if memory could not be allocated.
2457 ///
2458 /// q's allocator is used for temporary storage to speed up the multiplication.
2459 pub fn divTrunc(q: *Managed, r: *Managed, a: Const, b: Const) !void {2490 pub fn divTrunc(q: *Managed, r: *Managed, a: Const, b: Const) !void {
2460 try q.ensureCapacity(a.limbs.len + b.limbs.len + 1);2491 try q.ensureCapacity(a.limbs.len + b.limbs.len);
2461 try r.ensureCapacity(a.limbs.len);2492 try r.ensureCapacity(b.limbs.len);
2462 var mq = q.toMutable();2493 var mq = q.toMutable();
2463 var mr = r.toMutable();2494 var mr = r.toMutable();
2464 const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.limbs.len, b.limbs.len));2495 const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.limbs.len, b.limbs.len));
2465 defer q.allocator.free(limbs_buffer);2496 defer q.allocator.free(limbs_buffer);
2466 mq.divTrunc(&mr, a, b, limbs_buffer, q.allocator);2497 mq.divTrunc(&mr, a, b, limbs_buffer);
2467 q.setMetadata(mq.positive, mq.len);2498 q.setMetadata(mq.positive, mq.len);
2468 r.setMetadata(mr.positive, mr.len);2499 r.setMetadata(mr.positive, mr.len);
2469 }2500 }
...@@ -2893,20 +2924,22 @@ fn llmulaccLong(comptime op: AccOp, r: []Limb, a: []const Limb, b: []const Limb)...@@ -2893,20 +2924,22 @@ fn llmulaccLong(comptime op: AccOp, r: []Limb, a: []const Limb, b: []const Limb)
28932924
2894 var i: usize = 0;2925 var i: usize = 0;
2895 while (i < b.len) : (i += 1) {2926 while (i < b.len) : (i += 1) {
2896 llmulLimb(op, r[i..], a, b[i]);2927 _ = llmulLimb(op, r[i..], a, b[i]);
2897 }2928 }
2898}2929}
28992930
2900/// r = r (op) y * xi2931/// r = r (op) y * xi
2901/// The result is computed modulo `r.len`.2932/// The result is computed modulo `r.len`.
2902fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) void {2933/// Returns whether the operation overflowed.
2934fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) bool {
2903 @setRuntimeSafety(debug_safety);2935 @setRuntimeSafety(debug_safety);
2904 if (xi == 0) {2936 if (xi == 0) {
2905 return;2937 return false;
2906 }2938 }
29072939
2908 var a_lo = acc[0..y.len];2940 const split = std.math.min(y.len, acc.len);
2909 var a_hi = acc[y.len..];2941 var a_lo = acc[0..split];
2942 var a_hi = acc[split..];
29102943
2911 switch (op) {2944 switch (op) {
2912 .add => {2945 .add => {
...@@ -2920,6 +2953,8 @@ fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) void {...@@ -2920,6 +2953,8 @@ fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) void {
2920 while ((carry != 0) and (j < a_hi.len)) : (j += 1) {2953 while ((carry != 0) and (j < a_hi.len)) : (j += 1) {
2921 carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j]));2954 carry = @boolToInt(@addWithOverflow(Limb, a_hi[j], carry, &a_hi[j]));
2922 }2955 }
2956
2957 return carry != 0;
2923 },2958 },
2924 .sub => {2959 .sub => {
2925 var borrow: Limb = 0;2960 var borrow: Limb = 0;
...@@ -2932,6 +2967,8 @@ fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) void {...@@ -2932,6 +2967,8 @@ fn llmulLimb(comptime op: AccOp, acc: []Limb, y: []const Limb, xi: Limb) void {
2932 while ((borrow != 0) and (j < a_hi.len)) : (j += 1) {2967 while ((borrow != 0) and (j < a_hi.len)) : (j += 1) {
2933 borrow = @boolToInt(@subWithOverflow(Limb, a_hi[j], borrow, &a_hi[j]));2968 borrow = @boolToInt(@subWithOverflow(Limb, a_hi[j], borrow, &a_hi[j]));
2934 }2969 }
2970
2971 return borrow != 0;
2935 },2972 },
2936 }2973 }
2937}2974}
...@@ -3424,7 +3461,8 @@ fn llsquareBasecase(r: []Limb, x: []const Limb) void {...@@ -3424,7 +3461,8 @@ fn llsquareBasecase(r: []Limb, x: []const Limb) void {
34243461
3425 for (x_norm) |v, i| {3462 for (x_norm) |v, i| {
3426 // Accumulate all the x[i]*x[j] (with x!=j) products3463 // Accumulate all the x[i]*x[j] (with x!=j) products
3427 llmulLimb(.add, r[2 * i + 1 ..], x_norm[i + 1 ..], v);3464 const overflow = llmulLimb(.add, r[2 * i + 1 ..], x_norm[i + 1 ..], v);
3465 assert(!overflow);
3428 }3466 }
34293467
3430 // Each product appears twice, multiply by 23468 // Each product appears twice, multiply by 2
...@@ -3432,7 +3470,8 @@ fn llsquareBasecase(r: []Limb, x: []const Limb) void {...@@ -3432,7 +3470,8 @@ fn llsquareBasecase(r: []Limb, x: []const Limb) void {
34323470
3433 for (x_norm) |v, i| {3471 for (x_norm) |v, i| {
3434 // Compute and add the squares3472 // Compute and add the squares
3435 llmulLimb(.add, r[2 * i ..], x[i .. i + 1], v);3473 const overflow = llmulLimb(.add, r[2 * i ..], x[i .. i + 1], v);
3474 assert(!overflow);
3436 }3475 }
3437}3476}
34383477
lib/std/math/big/int_test.zig+1-1
...@@ -1016,7 +1016,7 @@ test "big.int mulWrap multi-multi unsigned" {...@@ -1016,7 +1016,7 @@ test "big.int mulWrap multi-multi unsigned" {
1016 defer c.deinit();1016 defer c.deinit();
1017 try c.mulWrap(a.toConst(), b.toConst(), .unsigned, 65);1017 try c.mulWrap(a.toConst(), b.toConst(), .unsigned, 65);
10181018
1019 try testing.expect((try c.to(u256)) == (op1 * op2) & ((1 << 65) - 1));1019 try testing.expect((try c.to(u128)) == (op1 * op2) & ((1 << 65) - 1));
1020}1020}
10211021
1022test "big.int mulWrap multi-multi signed" {1022test "big.int mulWrap multi-multi signed" {
src/value.zig+13-13
...@@ -2301,11 +2301,11 @@ pub const Value = extern union {...@@ -2301,11 +2301,11 @@ pub const Value = extern union {
2301 const rhs_bigint = rhs.toBigInt(&rhs_space);2301 const rhs_bigint = rhs.toBigInt(&rhs_space);
2302 const limbs_q = try allocator.alloc(2302 const limbs_q = try allocator.alloc(
2303 std.math.big.Limb,2303 std.math.big.Limb,
2304 lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,2304 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2305 );2305 );
2306 const limbs_r = try allocator.alloc(2306 const limbs_r = try allocator.alloc(
2307 std.math.big.Limb,2307 std.math.big.Limb,
2308 lhs_bigint.limbs.len,2308 rhs_bigint.limbs.len,
2309 );2309 );
2310 const limbs_buffer = try allocator.alloc(2310 const limbs_buffer = try allocator.alloc(
2311 std.math.big.Limb,2311 std.math.big.Limb,
...@@ -2313,7 +2313,7 @@ pub const Value = extern union {...@@ -2313,7 +2313,7 @@ pub const Value = extern union {
2313 );2313 );
2314 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };2314 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2315 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };2315 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2316 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);2316 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2317 const result_limbs = result_q.limbs[0..result_q.len];2317 const result_limbs = result_q.limbs[0..result_q.len];
23182318
2319 if (result_q.positive) {2319 if (result_q.positive) {
...@@ -2332,11 +2332,11 @@ pub const Value = extern union {...@@ -2332,11 +2332,11 @@ pub const Value = extern union {
2332 const rhs_bigint = rhs.toBigInt(&rhs_space);2332 const rhs_bigint = rhs.toBigInt(&rhs_space);
2333 const limbs_q = try allocator.alloc(2333 const limbs_q = try allocator.alloc(
2334 std.math.big.Limb,2334 std.math.big.Limb,
2335 lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,2335 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2336 );2336 );
2337 const limbs_r = try allocator.alloc(2337 const limbs_r = try allocator.alloc(
2338 std.math.big.Limb,2338 std.math.big.Limb,
2339 lhs_bigint.limbs.len,2339 rhs_bigint.limbs.len,
2340 );2340 );
2341 const limbs_buffer = try allocator.alloc(2341 const limbs_buffer = try allocator.alloc(
2342 std.math.big.Limb,2342 std.math.big.Limb,
...@@ -2344,7 +2344,7 @@ pub const Value = extern union {...@@ -2344,7 +2344,7 @@ pub const Value = extern union {
2344 );2344 );
2345 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };2345 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2346 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };2346 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2347 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);2347 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2348 const result_limbs = result_q.limbs[0..result_q.len];2348 const result_limbs = result_q.limbs[0..result_q.len];
23492349
2350 if (result_q.positive) {2350 if (result_q.positive) {
...@@ -2363,13 +2363,13 @@ pub const Value = extern union {...@@ -2363,13 +2363,13 @@ pub const Value = extern union {
2363 const rhs_bigint = rhs.toBigInt(&rhs_space);2363 const rhs_bigint = rhs.toBigInt(&rhs_space);
2364 const limbs_q = try allocator.alloc(2364 const limbs_q = try allocator.alloc(
2365 std.math.big.Limb,2365 std.math.big.Limb,
2366 lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,2366 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2367 );2367 );
2368 const limbs_r = try allocator.alloc(2368 const limbs_r = try allocator.alloc(
2369 std.math.big.Limb,2369 std.math.big.Limb,
2370 // TODO: audit this size, and also consider reworking Sema to re-use Values rather than2370 // TODO: consider reworking Sema to re-use Values rather than
2371 // always producing new Value objects.2371 // always producing new Value objects.
2372 rhs_bigint.limbs.len + 1,2372 rhs_bigint.limbs.len,
2373 );2373 );
2374 const limbs_buffer = try allocator.alloc(2374 const limbs_buffer = try allocator.alloc(
2375 std.math.big.Limb,2375 std.math.big.Limb,
...@@ -2377,7 +2377,7 @@ pub const Value = extern union {...@@ -2377,7 +2377,7 @@ pub const Value = extern union {
2377 );2377 );
2378 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };2378 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2379 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };2379 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2380 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);2380 result_q.divTrunc(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2381 const result_limbs = result_r.limbs[0..result_r.len];2381 const result_limbs = result_r.limbs[0..result_r.len];
23822382
2383 if (result_r.positive) {2383 if (result_r.positive) {
...@@ -2396,11 +2396,11 @@ pub const Value = extern union {...@@ -2396,11 +2396,11 @@ pub const Value = extern union {
2396 const rhs_bigint = rhs.toBigInt(&rhs_space);2396 const rhs_bigint = rhs.toBigInt(&rhs_space);
2397 const limbs_q = try allocator.alloc(2397 const limbs_q = try allocator.alloc(
2398 std.math.big.Limb,2398 std.math.big.Limb,
2399 lhs_bigint.limbs.len + rhs_bigint.limbs.len + 1,2399 lhs_bigint.limbs.len + rhs_bigint.limbs.len,
2400 );2400 );
2401 const limbs_r = try allocator.alloc(2401 const limbs_r = try allocator.alloc(
2402 std.math.big.Limb,2402 std.math.big.Limb,
2403 lhs_bigint.limbs.len,2403 rhs_bigint.limbs.len,
2404 );2404 );
2405 const limbs_buffer = try allocator.alloc(2405 const limbs_buffer = try allocator.alloc(
2406 std.math.big.Limb,2406 std.math.big.Limb,
...@@ -2408,7 +2408,7 @@ pub const Value = extern union {...@@ -2408,7 +2408,7 @@ pub const Value = extern union {
2408 );2408 );
2409 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };2409 var result_q = BigIntMutable{ .limbs = limbs_q, .positive = undefined, .len = undefined };
2410 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };2410 var result_r = BigIntMutable{ .limbs = limbs_r, .positive = undefined, .len = undefined };
2411 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer, null);2411 result_q.divFloor(&result_r, lhs_bigint, rhs_bigint, limbs_buffer);
2412 const result_limbs = result_r.limbs[0..result_r.len];2412 const result_limbs = result_r.limbs[0..result_r.len];
24132413
2414 if (result_r.positive) {2414 if (result_r.positive) {