From e42cb3b2369b3034c15be3a6f0bf519db4baac98 Mon Sep 17 00:00:00 2001 From: Pavel Verigo Date: Wed, 8 Jul 2026 01:52:46 +0200 Subject: [PATCH] std.math.big: update divCeil add tests --- lib/std/math/big/int.zig | 37 +++++++++++++++++++++------------- lib/std/math/big/int_test.zig | 38 +++++++++++++++++++++++++++++++++++ 2 files changed, 61 insertions(+), 14 deletions(-) diff --git a/lib/std/math/big/int.zig b/lib/std/math/big/int.zig index 8765f5de2d1944101a6a5a5fec9acaac473b6c06..5f33b91b52a95b0e699b5db8e247c9d1390083d1 100644 --- a/lib/std/math/big/int.zig +++ b/lib/std/math/big/int.zig @@ -1260,8 +1260,8 @@ pub const Mutable = struct { // If the result is negative then the default truncating division already rounds // towards positive infinity, so no adjustment is needed. // If the remainder is 0 then the division is exact and no adjustment is needed. - } else if (a.positive) { - // Both positive. + } else { + // Same sign. // We have: // modCeil(a, b) != 0 // => @divCeil(a, b) = @divTrunc(a, b) + 1 @@ -1270,20 +1270,10 @@ pub const Mutable = struct { // b * @divCeil(a, b) + modCeil(a, b) = a // => b * @divTrunc(a, b) + b + modCeil(a, b) = a // => modCeil(a, b) = @rem(a, b) - b + // + // This works for both positive and negative b because b keeps its sign. q.addScalar(q.toConst(), 1); r.sub(r.toConst(), y.toConst()); - } else { - // Both negative. - // We have: - // modCeil(-a, -b) != 0 - // => @divCeil(-a, -b) = @divTrunc(-a, -b) + 1 - // And: - // -b * @divTrunc(-a, -b) + @rem(-a, -b) = -a - // -b * @divCeil(-a, -b) + modCeil(-a, -b) = -a - // => -b * @divTrunc(-a, -b) - b + modCeil(-a, -b) = -a - // => modCeil(-a, -b) = @rem(-a, -b) + b - q.addScalar(q.toConst(), 1); - r.add(r.toConst(), y.toConst().abs()); } } @@ -3380,6 +3370,25 @@ pub const Managed = struct { r.setMetadata(mr.positive, mr.len); } + /// q = a / b (rem r) + /// + /// a / b are ceiled (rounded towards positive infinity). + /// + /// Returns an error if memory could not be allocated. + pub fn divCeil(q: *Managed, r: *Managed, a: *const Managed, b: *const Managed) !void { + const q_alias = limbsAliasDistinct(q, a) or limbsAliasDistinct(q, b); + const r_alias = limbsAliasDistinct(r, a) or limbsAliasDistinct(r, b); + try q.ensureAliasAwareCapacity(a.len(), q_alias); + try r.ensureAliasAwareCapacity(b.len(), r_alias); + var mq = q.toMutable(); + var mr = r.toMutable(); + const limbs_buffer = try q.allocator.alloc(Limb, calcDivLimbsBufferLen(a.len(), b.len())); + defer q.allocator.free(limbs_buffer); + mq.divCeil(&mr, a.toConst(), b.toConst(), limbs_buffer); + q.setMetadata(mq.positive, mq.len); + r.setMetadata(mr.positive, mr.len); + } + /// q = a / b (rem r) /// /// a / b are truncated (rounded towards -inf). diff --git a/lib/std/math/big/int_test.zig b/lib/std/math/big/int_test.zig index 0bc1acd87670ae03cd3d2d957dc6e696dde31a19..485ae4918cce7545119b5b014f7c8133656aaf0b 100644 --- a/lib/std/math/big/int_test.zig +++ b/lib/std/math/big/int_test.zig @@ -2127,6 +2127,44 @@ test "div floor positive close to zero" { try testing.expectEqual(10, try r.toInt(i32)); } +fn testDivCeil(comptime T: type, u: T, v: T, eq: T, er: T) !void { + var a = try Managed.initSet(testing.allocator, u); + defer a.deinit(); + var b = try Managed.initSet(testing.allocator, v); + defer b.deinit(); + + var q = try Managed.init(testing.allocator); + defer q.deinit(); + var r = try Managed.init(testing.allocator); + defer r.deinit(); + + try Managed.divCeil(&q, &r, &a, &b); + + try testing.expectEqual(eq, try q.toInt(T)); + try testing.expectEqual(er, try r.toInt(T)); +} + +test "div ceil small" { + try testDivCeil(i32, 5, 3, 2, -1); + try testDivCeil(i32, -5, 3, -1, -2); + try testDivCeil(i32, 5, -3, -1, 2); + try testDivCeil(i32, -5, -3, 2, 1); + try testDivCeil(i32, -0x80000000, 1, -0x80000000, 0); +} + +test "div ceil multi-limb" { + { + const a = (@as(i128, 1) << 100) + 3; + const b: i128 = 4; + try testDivCeil(i128, a, b, (1 << 98) + 1, -1); + } + { + const a = -((@as(i128, 1) << 100) + 3); + const b: i128 = 4; + try testDivCeil(i128, a, b, -(1 << 98), -3); + } +} + test "div multi-multi with rem" { if (builtin.zig_backend == .stage2_c) return error.SkipZigTest; -- 2.54.0