authorgravatar for codroid@gmail.comStevie Hryciw <codroid@gmail.com> 2022-11-16 23:10:43-08:00
committergravatar for git@vexu.euVeikka Tuominen <git@vexu.eu> 2022-11-18 14:31:30+02:00
log5f6f38ff3160c75c03ce0477904051e6d8af0c80
treeaeb86f150ecd3d0183abb6531fc31c1040d2481d
parent5221c90164b80b61ef1d113a44126b9fab74a916

std.math.big.int: implement popCount() for Const


2 files changed, 123 insertions(+), 5 deletions(-)

lib/std/math/big/int.zig+46
......@@ -1959,6 +1959,52 @@ pub const Const = struct {
19591959 return bits;
19601960 }
19611961
1962 /// @popCount with two's complement semantics.
1963 ///
1964 /// This returns the number of 1 bits set when the value would be represented in
1965 /// two's complement with the given integer width (bit_count).
1966 /// This includes the leading sign bit, which will be set for negative values.
1967 ///
1968 /// Asserts that bit_count is enough to represent value in two's compliment
1969 /// and that the final result fits in a usize.
1970 /// Asserts that there are no trailing empty limbs on the most significant end,
1971 /// i.e. that limb count matches `calcLimbLen()` and zero is not negative.
1972 pub fn popCount(self: Const, bit_count: usize) usize {
1973 var sum: usize = 0;
1974 if (self.positive) {
1975 for (self.limbs) |limb| {
1976 sum += @popCount(limb);
1977 }
1978 } else {
1979 assert(self.fitsInTwosComp(.signed, bit_count));
1980 assert(self.limbs[self.limbs.len - 1] != 0);
1981
1982 var remaining_bits = bit_count;
1983 var carry: u1 = 1;
1984 var add_res: Limb = undefined;
1985
1986 // All but the most significant limb.
1987 for (self.limbs[0 .. self.limbs.len - 1]) |limb| {
1988 carry = @boolToInt(@addWithOverflow(Limb, ~limb, carry, &add_res));
1989 sum += @popCount(add_res);
1990 remaining_bits -= limb_bits; // Asserted not to undeflow by fitsInTwosComp
1991 }
1992
1993 // The most significant limb may have fewer than @bitSizeOf(Limb) meaningful bits,
1994 // which we can detect with @clz().
1995 // There may also be fewer limbs than needed to fill bit_count.
1996 const limb = self.limbs[self.limbs.len - 1];
1997 const leading_zeroes = @clz(limb);
1998 // The most significant limb is asserted not to be all 0s (above),
1999 // so ~limb cannot be all 1s, and ~limb + 1 cannot overflow.
2000 sum += @popCount(~limb + carry);
2001 sum -= leading_zeroes; // All leading zeroes were flipped and added to sum, so undo those
2002 const remaining_ones = remaining_bits - (limb_bits - leading_zeroes); // All bits not covered by limbs
2003 sum += remaining_ones;
2004 }
2005 return sum;
2006 }
2007
19622008 pub fn fitsInTwosComp(self: Const, signedness: Signedness, bit_count: usize) bool {
19632009 if (self.eqZero()) {
19642010 return true;
lib/std/math/big/int_test.zig+77-5
......@@ -2578,14 +2578,86 @@ test "big.int regression test for realloc with alias" {
25782578}
25792579
25802580test "big int popcount" {
2581 var a = try Managed.initSet(testing.allocator, -1);
2581 var a = try Managed.init(testing.allocator);
25822582 defer a.deinit();
2583 var b = try Managed.initSet(testing.allocator, -1);
2584 defer b.deinit();
25852583
2586 try a.popCount(&b, 16);
2584 try a.set(0);
2585 try popCountTest(&a, 0, 0);
2586 try popCountTest(&a, 567, 0);
2587 try a.set(-0);
2588 try popCountTest(&a, 0, 0);
2589
2590 try a.set(1);
2591 try popCountTest(&a, 1, 1);
2592 try popCountTest(&a, 13, 1);
2593 try popCountTest(&a, 432, 1);
2594
2595 try a.set(255);
2596 try popCountTest(&a, 8, 8);
2597 try a.set(-128);
2598 try popCountTest(&a, 8, 1);
2599
2600 try a.set(-2);
2601 try popCountTest(&a, 16, 15);
2602 try popCountTest(&a, 15, 14);
2603
2604 try a.set(-2047);
2605 try popCountTest(&a, 12, 2);
2606 try popCountTest(&a, 24, 14);
2607
2608 try a.set(maxInt(u5000));
2609 try popCountTest(&a, 5000, 5000);
2610 try a.set(minInt(i5000));
2611 try popCountTest(&a, 5000, 1);
2612
2613 // Check -1 at various bit counts that cross Limb size multiples.
2614 const limb_bits = @bitSizeOf(Limb);
2615 try a.set(-1);
2616 try popCountTest(&a, 1, 1); // i1
2617 try popCountTest(&a, 2, 2);
2618 try popCountTest(&a, 16, 16);
2619 try popCountTest(&a, 543, 543);
2620 try popCountTest(&a, 544, 544);
2621 try popCountTest(&a, limb_bits - 1, limb_bits - 1);
2622 try popCountTest(&a, limb_bits, limb_bits);
2623 try popCountTest(&a, limb_bits + 1, limb_bits + 1);
2624 try popCountTest(&a, limb_bits * 2 - 1, limb_bits * 2 - 1);
2625 try popCountTest(&a, limb_bits * 2, limb_bits * 2);
2626 try popCountTest(&a, limb_bits * 2 + 1, limb_bits * 2 + 1);
2627
2628 // Check very large numbers.
2629 try a.setString(16, "ff00000100000100" ++ ("0000000000000000" ** 62));
2630 try popCountTest(&a, 4032, 10);
2631 try popCountTest(&a, 6000, 10);
2632 a.negate();
2633 try popCountTest(&a, 4033, 48);
2634 try popCountTest(&a, 4133, 148);
2635
2636 // Check when most significant limb is full of 1s.
2637 const limb_size = @bitSizeOf(Limb);
2638 try a.set(maxInt(Limb));
2639 try popCountTest(&a, limb_size, limb_size);
2640 try popCountTest(&a, limb_size + 1, limb_size);
2641 try popCountTest(&a, limb_size * 10 + 2, limb_size);
2642 a.negate();
2643 try popCountTest(&a, limb_size * 2 - 2, limb_size - 1);
2644 try popCountTest(&a, limb_size * 2 - 1, limb_size);
2645 try popCountTest(&a, limb_size * 2, limb_size + 1);
2646 try popCountTest(&a, limb_size * 2 + 1, limb_size + 2);
2647 // TODO: These produce incorrect pop count for Mutable
2648 // https://github.com/ziglang/zig/issues/13571
2649 // try popCountTest(&a, limb_size * 2 + 2, limb_size + 3);
2650 // try popCountTest(&a, limb_size * 2 + 3, limb_size + 4);
2651 // try popCountTest(&a, limb_size * 2 + 4, limb_size + 5);
2652}
2653
2654fn popCountTest(val: *const Managed, bit_count: usize, expected: usize) !void {
2655 var b = try Managed.init(testing.allocator);
2656 defer b.deinit();
2657 try b.popCount(val, bit_count);
25872658
2588 try testing.expect(a.toConst().orderAgainstScalar(16) == .eq);
2659 try testing.expectEqual(std.math.Order.eq, b.toConst().orderAgainstScalar(expected));
2660 try testing.expectEqual(expected, val.toConst().popCount(bit_count));
25892661}
25902662
25912663test "big int conversion read/write twos complement" {