From 3c7e154e7f8d0441253ff6a25995b9924d880154 Mon Sep 17 00:00:00 2001 From: Sijawusz Pur Rahnama Date: Fri, 8 Mar 2019 02:10:34 +0100 Subject: [PATCH] Implement Int#{leading,trailing}_zeros_count --- spec/std/big/big_int_spec.cr | 4 ++ spec/std/int_spec.cr | 16 +++++++ src/big/big_int.cr | 8 ++++ src/big/lib_gmp.cr | 2 + src/int.cr | 86 ++++++++++++++++++++++++++++++++++++ src/intrinsics.cr | 12 +++++ 6 files changed, 128 insertions(+) diff --git a/spec/std/big/big_int_spec.cr b/spec/std/big/big_int_spec.cr index ccfcdd4466d5..497a2dbff8fc 100644 --- a/spec/std/big/big_int_spec.cr +++ b/spec/std/big/big_int_spec.cr @@ -326,6 +326,10 @@ describe "BigInt" do 5.to_big_i.popcount.should eq(2) end + it "#trailing_zeros_count" do + "00000000000000001000000000001000".to_big_i(base: 2).trailing_zeros_count.should eq(3) + end + it "#hash" do b1 = 5.to_big_i b2 = 5.to_big_i diff --git a/spec/std/int_spec.cr b/spec/std/int_spec.cr index 0a1c5abf4db2..f0d71f460d12 100644 --- a/spec/std/int_spec.cr +++ b/spec/std/int_spec.cr @@ -646,6 +646,22 @@ describe "Int" do it { 18446744073709551615_u64.popcount.should eq(64) } end + describe "#leading_zeros_count" do + {% for width in %w(8 16 32 64).map(&.id) %} + it { -1_i{{width}}.leading_zeros_count.should eq(0) } + it { 0_i{{width}}.leading_zeros_count.should eq({{width}}) } + it { 0_u{{width}}.leading_zeros_count.should eq({{width}}) } + {% end %} + end + + describe "#trailing_zeros_count" do + {% for width in %w(8 16 32 64).map(&.id) %} + it { -2_i{{width}}.trailing_zeros_count.should eq(1) } + it { 2_i{{width}}.trailing_zeros_count.should eq(1) } + it { 2_u{{width}}.trailing_zeros_count.should eq(1) } + {% end %} + end + it "compares signed vs. unsigned integers" do signed_ints = [Int8::MAX, Int16::MAX, Int32::MAX, Int64::MAX, Int8::MIN, Int16::MIN, Int32::MIN, Int64::MIN, 0_i8, 0_i16, 0_i32, 0_i64] unsigned_ints = [UInt8::MAX, UInt16::MAX, UInt32::MAX, UInt64::MAX, 0_u8, 0_u16, 0_u32, 0_u64] diff --git a/src/big/big_int.cr b/src/big/big_int.cr index 8b0b71533f51..52d134e8fa0b 100644 --- a/src/big/big_int.cr +++ b/src/big/big_int.cr @@ -385,6 +385,14 @@ struct BigInt < Int LibGMP.popcount(self) end + def leading_zeros_count + {% raise "No BigInt implementation available" %} + end + + def trailing_zeros_count + LibGMP.scan1(self, 0) + end + def to_i to_i32 end diff --git a/src/big/lib_gmp.cr b/src/big/lib_gmp.cr index 6104825dbbb6..59c4ee464f7b 100644 --- a/src/big/lib_gmp.cr +++ b/src/big/lib_gmp.cr @@ -93,6 +93,8 @@ lib LibGMP # # Logic fun popcount = __gmpz_popcount(op : MPZ*) : BitcntT + fun scan0 = __gmpz_scan0(op : MPZ*, starting_bit : BitcntT) : BitcntT + fun scan1 = __gmpz_scan1(op : MPZ*, starting_bit : BitcntT) : BitcntT # # Comparison diff --git a/src/int.cr b/src/int.cr index 64758aa4c82b..eb408cbb672f 100644 --- a/src/int.cr +++ b/src/int.cr @@ -542,6 +542,12 @@ struct Int # ``` abstract def popcount + # Returns the number of leading `0`-bits. + # abstract def leading_zeros_count + + # Returns the number of trailing `0`-bits. + abstract def trailing_zeros_count + private class TimesIterator(T) include Iterator(T) @@ -629,6 +635,14 @@ struct Int8 Intrinsics.popcount8(self) end + def leading_zeros_count + Intrinsics.countleading8(self, false) + end + + def trailing_zeros_count + Intrinsics.counttrailing8(self, false) + end + def clone self end @@ -656,6 +670,14 @@ struct Int16 Intrinsics.popcount16(self) end + def leading_zeros_count + Intrinsics.countleading16(self, false) + end + + def trailing_zeros_count + Intrinsics.counttrailing16(self, false) + end + def clone self end @@ -683,6 +705,14 @@ struct Int32 Intrinsics.popcount32(self) end + def leading_zeros_count + Intrinsics.countleading32(self, false) + end + + def trailing_zeros_count + Intrinsics.counttrailing32(self, false) + end + def clone self end @@ -710,6 +740,14 @@ struct Int64 Intrinsics.popcount64(self) end + def leading_zeros_count + Intrinsics.countleading64(self, false) + end + + def trailing_zeros_count + Intrinsics.counttrailing64(self, false) + end + def clone self end @@ -739,6 +777,14 @@ struct Int128 Intrinsics.popcount128(self) end + def leading_zeros_count + Intrinsics.countleading128(self, false) + end + + def trailing_zeros_count + Intrinsics.counttrailing128(self, false) + end + def clone self end @@ -766,6 +812,14 @@ struct UInt8 Intrinsics.popcount8(self) end + def leading_zeros_count + Intrinsics.countleading8(self, false) + end + + def trailing_zeros_count + Intrinsics.counttrailing8(self, false) + end + def clone self end @@ -793,6 +847,14 @@ struct UInt16 Intrinsics.popcount16(self) end + def leading_zeros_count + Intrinsics.countleading16(self, false) + end + + def trailing_zeros_count + Intrinsics.counttrailing16(self, false) + end + def clone self end @@ -820,6 +882,14 @@ struct UInt32 Intrinsics.popcount32(self) end + def leading_zeros_count + Intrinsics.countleading32(self, false) + end + + def trailing_zeros_count + Intrinsics.counttrailing32(self, false) + end + def clone self end @@ -847,6 +917,14 @@ struct UInt64 Intrinsics.popcount64(self) end + def leading_zeros_count + Intrinsics.countleading64(self, false) + end + + def trailing_zeros_count + Intrinsics.counttrailing64(self, false) + end + def clone self end @@ -875,6 +953,14 @@ struct UInt128 Intrinsics.popcount128(self) end + def leading_zeros_count + Intrinsics.countleading128(self, false) + end + + def trailing_zeros_count + Intrinsics.counttrailing128(self, false) + end + def clone self end diff --git a/src/intrinsics.cr b/src/intrinsics.cr index edf4f14548cb..d47781a15e16 100644 --- a/src/intrinsics.cr +++ b/src/intrinsics.cr @@ -18,6 +18,18 @@ lib Intrinsics fun popcount64 = "llvm.ctpop.i64"(src : Int64) : Int64 fun popcount128 = "llvm.ctpop.i128"(src : Int128) : Int128 + fun countleading8 = "llvm.ctlz.i8"(src : Int8, zero_is_undef : Bool) : Int8 + fun countleading16 = "llvm.ctlz.i16"(src : Int16, zero_is_undef : Bool) : Int16 + fun countleading32 = "llvm.ctlz.i32"(src : Int32, zero_is_undef : Bool) : Int32 + fun countleading64 = "llvm.ctlz.i64"(src : Int64, zero_is_undef : Bool) : Int64 + fun countleading128 = "llvm.ctlz.i128"(src : Int128, zero_is_undef : Bool) : Int128 + + fun counttrailing8 = "llvm.cttz.i8"(src : Int8, zero_is_undef : Bool) : Int8 + fun counttrailing16 = "llvm.cttz.i16"(src : Int16, zero_is_undef : Bool) : Int16 + fun counttrailing32 = "llvm.cttz.i32"(src : Int32, zero_is_undef : Bool) : Int32 + fun counttrailing64 = "llvm.cttz.i64"(src : Int64, zero_is_undef : Bool) : Int64 + fun counttrailing128 = "llvm.cttz.i128"(src : Int128, zero_is_undef : Bool) : Int128 + fun va_start = "llvm.va_start"(ap : Void*) fun va_end = "llvm.va_end"(ap : Void*) end