authorgravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-07-08 01:52:46+02:00
committergravatar for paul.verigo@gmail.comPavel Verigo <paul.verigo@gmail.com> 2026-07-08 01:52:46+02:00
loge42cb3b2369b3034c15be3a6f0bf519db4baac98
tree7ca2c7826fdcf18c6e33106809c9594ea4d11c98
parentb4c49390434ac1905ed7b962a26db110daafbd15

std.math.big: update divCeil add tests


2 files changed, 61 insertions(+), 14 deletions(-)

lib/std/math/big/int.zig+23-14
...@@ -1260,8 +1260,8 @@ pub const Mutable = struct {...@@ -1260,8 +1260,8 @@ pub const Mutable = struct {
1260 // If the result is negative then the default truncating division already rounds1260 // If the result is negative then the default truncating division already rounds
1261 // towards positive infinity, so no adjustment is needed.1261 // towards positive infinity, so no adjustment is needed.
1262 // If the remainder is 0 then the division is exact and no adjustment is needed.1262 // If the remainder is 0 then the division is exact and no adjustment is needed.
1263 } else if (a.positive) {1263 } else {
1264 // Both positive.1264 // Same sign.
1265 // We have:1265 // We have:
1266 // modCeil(a, b) != 01266 // modCeil(a, b) != 0
1267 // => @divCeil(a, b) = @divTrunc(a, b) + 11267 // => @divCeil(a, b) = @divTrunc(a, b) + 1
...@@ -1270,20 +1270,10 @@ pub const Mutable = struct {...@@ -1270,20 +1270,10 @@ pub const Mutable = struct {
1270 // b * @divCeil(a, b) + modCeil(a, b) = a1270 // b * @divCeil(a, b) + modCeil(a, b) = a
1271 // => b * @divTrunc(a, b) + b + modCeil(a, b) = a1271 // => b * @divTrunc(a, b) + b + modCeil(a, b) = a
1272 // => modCeil(a, b) = @rem(a, b) - b1272 // => modCeil(a, b) = @rem(a, b) - b
1273 //
1274 // This works for both positive and negative b because b keeps its sign.
1273 q.addScalar(q.toConst(), 1);1275 q.addScalar(q.toConst(), 1);
1274 r.sub(r.toConst(), y.toConst());1276 r.sub(r.toConst(), y.toConst());
1275 } else {
1276 // Both negative.
1277 // We have:
1278 // modCeil(-a, -b) != 0
1279 // => @divCeil(-a, -b) = @divTrunc(-a, -b) + 1
1280 // And:
1281 // -b * @divTrunc(-a, -b) + @rem(-a, -b) = -a
1282 // -b * @divCeil(-a, -b) + modCeil(-a, -b) = -a
1283 // => -b * @divTrunc(-a, -b) - b + modCeil(-a, -b) = -a
1284 // => modCeil(-a, -b) = @rem(-a, -b) + b
1285 q.addScalar(q.toConst(), 1);
1286 r.add(r.toConst(), y.toConst().abs());
1287 }1277 }
1288 }1278 }
12891279
...@@ -3380,6 +3370,25 @@ pub const Managed = struct {...@@ -3380,6 +3370,25 @@ pub const Managed = struct {
3380 r.setMetadata(mr.positive, mr.len);3370 r.setMetadata(mr.positive, mr.len);
3381 }3371 }
33823372
3373 /// q = a / b (rem r)
3374 ///
3375 /// a / b are ceiled (rounded towards positive infinity).
3376 ///
3377 /// Returns an error if memory could not be allocated.
3378 pub fn divCeil(q: *Managed, r: *Managed, a: *const Managed, b: *const Managed) !void {
3379 const q_alias = limbsAliasDistinct(q, a) or limbsAliasDistinct(q, b);
3380 const r_alias = limbsAliasDistinct(r, a) or limbsAliasDistinct(r, b);
3381 try q.ensureAliasAwareCapacity(a.len(), q_alias);
3382 try r.ensureAliasAwareCapacity(b.len(), r_alias);
3383 var mq = q.toMutable();
3384 var mr = r.toMutable();
3385 const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.len(), b.len()));
3386 defer q.allocator.free(limbs_buffer);
3387 mq.divCeil(&mr, a.toConst(), b.toConst(), limbs_buffer);
3388 q.setMetadata(mq.positive, mq.len);
3389 r.setMetadata(mr.positive, mr.len);
3390 }
3391
3383 /// q = a / b (rem r)3392 /// q = a / b (rem r)
3384 ///3393 ///
3385 /// a / b are truncated (rounded towards -inf).3394 /// a / b are truncated (rounded towards -inf).
lib/std/math/big/int_test.zig+38
...@@ -2127,6 +2127,44 @@ test "div floor positive close to zero" {...@@ -2127,6 +2127,44 @@ test "div floor positive close to zero" {
2127 try testing.expectEqual(10, try r.toInt(i32));2127 try testing.expectEqual(10, try r.toInt(i32));
2128}2128}
21292129
2130fn testDivCeil(comptime T: type, u: T, v: T, eq: T, er: T) !void {
2131 var a = try Managed.initSet(testing.allocator, u);
2132 defer a.deinit();
2133 var b = try Managed.initSet(testing.allocator, v);
2134 defer b.deinit();
2135
2136 var q = try Managed.init(testing.allocator);
2137 defer q.deinit();
2138 var r = try Managed.init(testing.allocator);
2139 defer r.deinit();
2140
2141 try Managed.divCeil(&q, &r, &a, &b);
2142
2143 try testing.expectEqual(eq, try q.toInt(T));
2144 try testing.expectEqual(er, try r.toInt(T));
2145}
2146
2147test "div ceil small" {
2148 try testDivCeil(i32, 5, 3, 2, -1);
2149 try testDivCeil(i32, -5, 3, -1, -2);
2150 try testDivCeil(i32, 5, -3, -1, 2);
2151 try testDivCeil(i32, -5, -3, 2, 1);
2152 try testDivCeil(i32, -0x80000000, 1, -0x80000000, 0);
2153}
2154
2155test "div ceil multi-limb" {
2156 {
2157 const a = (@as(i128, 1) << 100) + 3;
2158 const b: i128 = 4;
2159 try testDivCeil(i128, a, b, (1 << 98) + 1, -1);
2160 }
2161 {
2162 const a = -((@as(i128, 1) << 100) + 3);
2163 const b: i128 = 4;
2164 try testDivCeil(i128, a, b, -(1 << 98), -3);
2165 }
2166}
2167
2130test "div multi-multi with rem" {2168test "div multi-multi with rem" {
2131 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;2169 if (builtin.zig_backend == .stage2_c) return error.SkipZigTest;
21322170