bit_counter 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 5bc1b5aaafc6fa30829b05e4968696aad08cf8a0
4
+ data.tar.gz: fb66250600e1af2c144b2295ee18936d4fde64b4
5
+ SHA512:
6
+ metadata.gz: 6ed9f143cb926c71d5ae1ffda8d52a8aa72f30c41d266ee1c7fe23ed5ecc144c04a413fad34dcdfd11dd9811456fcca893176a029426ba385bc10b06ff6ed552
7
+ data.tar.gz: 9cb5f2ce80ccbcb7250cf75413bf814908e32390794a0cd548268e63359f1c090470ba703e6f498843ccf03e90bffc4fb371141d4dfda65141193647d036c97e
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
15
+ /vendor/
16
+ /ext/bit_counter/Makefile
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,10 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - 2.0.0
5
+ - 2.1.6
6
+ - 2.2.4
7
+ - 2.3.0
8
+ - jruby-9.0.4.0
9
+ - rbx-3.27
10
+ before_install: gem install bundler -v 1.11.2
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in bit_counter.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2016 Jkr2255
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,100 @@
1
+ # BitCounter
2
+
3
+ Counting bits (popcount, Hamming weight) in integer, made faster.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'bit_counter'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install bit_counter
20
+
21
+ ## Usage
22
+
23
+ Module `BitCounter` appears after `require 'bit_counter'`.
24
+
25
+ `BitCounter.count(num)` returns bit count of `num`. (`num` must be `Integer` (`Fixnum` or `Bignum`); otherwise raises `TypeError`)
26
+
27
+ `BitCounter.count_fixnum(num)` and `BitCounter.count_bignum(num)` are provided, but directly using these are not recommended.
28
+
29
+ To use `Integer#bit_count`, `require 'bit_counter/core_ext'`. (This feature is not default behavior)
30
+
31
+ ### for negative numbers
32
+ Theoretically, Ruby treats negative integers as if *infinite* 1-bits are leaded (`printf '%x', -1` gives `..f` ),
33
+ so counting 1 bits for negative numbers is meaningless.
34
+
35
+ In this implementation, `bit_count` for negative number counts *0* bits and negates the result. (distinguishing with positive version) Same in Ruby:
36
+ ```rb
37
+ return -bit_count(~num) if num < 0
38
+ ```
39
+
40
+ Note that both `bit_count(0)` and `bit_count(-1)` still return 0.
41
+
42
+ ## Development
43
+
44
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
45
+
46
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
47
+
48
+ ## Implementations
49
+
50
+ ### JRuby
51
+ Java offers `Long.bitCount`, so simply calling this for `Fixnum`.
52
+
53
+ JRuby uses [`java.lang.BigInteger`](https://docs.oracle.com/javase/7/docs/api/java/math/BigInteger.html) for Ruby's `Bignum`, which also has `bitCount()` method.
54
+
55
+ ### CRuby & Rubinius
56
+ In C extensions, `Bignum` is converted to array of `long`, and bit is counted using loops.
57
+ Bit counting of `long` is done by using `POPCNT` instruction, if available.
58
+
59
+ ### Limitations
60
+ This gem is mainly developed on x64 GCC environment, so in other environments it may not work or be slow.
61
+
62
+ Pull requests for other environments are welcome.
63
+
64
+ ## Benchmark
65
+ Compared to `num.to_s(2).count('1')` (popular method for popcount in CRuby), this gem is 5x - 20x faster.
66
+
67
+ ```
68
+ # for fixnum
69
+ user system total real
70
+ String 7.770000 0.000000 7.770000 ( 7.771210)
71
+ Arithmetic 4.950000 0.000000 4.950000 ( 4.953082)
72
+ Loop 9.860000 0.000000 9.860000 ( 9.863400)
73
+ Gem 0.610000 0.000000 0.610000 ( 0.612627)
74
+
75
+ # for bignum
76
+ for 280736 bits, 1000 times
77
+ user system total real
78
+ String 1.470000 0.030000 1.500000 ( 1.491823)
79
+ Pack 8.810000 0.000000 8.810000 ( 8.823835)
80
+ Gem 0.040000 0.000000 0.040000 ( 0.032927)
81
+
82
+ for 80 bits, 1000000 times
83
+ user system total real
84
+ String 0.820000 0.000000 0.820000 ( 0.827302)
85
+ Pack 3.860000 0.000000 3.860000 ( 3.869715)
86
+ Gem 0.160000 0.000000 0.160000 ( 0.150728)
87
+
88
+ ```
89
+
90
+ (Done in Core i3 (with POPCNT instruction), Linux x64, Ruby 2.3.0)
91
+
92
+ ## Contributing
93
+
94
+ Bug reports and pull requests are welcome on GitHub at https://github.com/jkr2255/bit_counter.
95
+
96
+
97
+ ## License
98
+
99
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
100
+
data/Rakefile ADDED
@@ -0,0 +1,18 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ if RUBY_PLATFORM =~ /java/
7
+ task :default => [:spec]
8
+ else
9
+ require "rake/extensiontask"
10
+
11
+ task :build => :compile
12
+
13
+ Rake::ExtensionTask.new("bit_counter") do |ext|
14
+ ext.lib_dir = "lib/bit_counter"
15
+ end
16
+
17
+ task :default => [:clobber, :compile, :spec]
18
+ end
@@ -0,0 +1,65 @@
1
+ require 'bit_counter'
2
+ require 'benchmark'
3
+
4
+ def count_string(num)
5
+ return -count_string(~num) if num < 0
6
+ num.to_s(2).count '1'
7
+ end
8
+
9
+ $table = 128.times.map { |i| BitCounter.count(i) }
10
+
11
+ def count_from_pack(num)
12
+ return -count_string(~num) if num < 0
13
+ [num].pack('w').chars.reduce(0) { |cnt, c| cnt + $table[c.ord & 0x7f] }
14
+ end
15
+
16
+
17
+ value = 7**100000
18
+ count = 1_000
19
+
20
+ if value.respond_to?(:bit_length)
21
+ puts "for #{value.bit_length} bits, #{count} times"
22
+ end
23
+
24
+ Benchmark.bm 10 do |r|
25
+ r.report "String" do
26
+ count.times do
27
+ count_string(value)
28
+ end
29
+ end
30
+ r.report "Pack" do
31
+ count.times do
32
+ count_from_pack(value)
33
+ end
34
+ end
35
+ r.report "C" do
36
+ count.times do
37
+ BitCounter.count_bignum(value)
38
+ end
39
+ end
40
+ end
41
+
42
+ value = 2**80 - 6
43
+ count = 1_000_000
44
+
45
+ if value.respond_to?(:bit_length)
46
+ puts "for #{value.bit_length} bits, #{count} times"
47
+ end
48
+
49
+ Benchmark.bm 10 do |r|
50
+ r.report "String" do
51
+ count.times do
52
+ count_string(value)
53
+ end
54
+ end
55
+ r.report "Pack" do
56
+ count.times do
57
+ count_from_pack(value)
58
+ end
59
+ end
60
+ r.report "C" do
61
+ count.times do
62
+ BitCounter.count_bignum(value)
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,55 @@
1
+ require 'bit_counter'
2
+ require 'benchmark'
3
+
4
+ def count_string(num)
5
+ return -count_string(~num) if num < 0
6
+ num.to_s(2).count '1'
7
+ end
8
+
9
+ def count_arithmetic_32(x)
10
+ return -count_string(~x) if x < 0
11
+ m1 = 0x55555555
12
+ m2 = 0x33333333
13
+ m4 = 0x0f0f0f0f
14
+ x -= (x >> 1) & m1
15
+ x = (x & m2) + ((x >> 2) & m2)
16
+ x = (x + (x >> 4)) & m4
17
+ x += x >> 8
18
+ (x + (x >> 16)) & 0x3f
19
+ end
20
+
21
+ def count_bit_loop(x)
22
+ return -count_string(~x) if x < 0
23
+ ret = 0
24
+ while x > 0
25
+ ret += 1
26
+ x &= x - 1
27
+ end
28
+ ret
29
+ end
30
+
31
+ value = 0x3EDC_BA98
32
+ count = 10_000_000
33
+
34
+ Benchmark.bm 10 do |r|
35
+ r.report "String" do
36
+ count.times do
37
+ count_string(value)
38
+ end
39
+ end
40
+ r.report "Arithmetic" do
41
+ count.times do
42
+ count_arithmetic_32(value)
43
+ end
44
+ end
45
+ r.report "Loop" do
46
+ count.times do
47
+ count_bit_loop(value)
48
+ end
49
+ end
50
+ r.report "Native" do
51
+ count.times do
52
+ BitCounter.count_fixnum(value)
53
+ end
54
+ end
55
+ end
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "bit_counter"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,30 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'bit_counter/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "bit_counter"
8
+ spec.version = BitCounter::VERSION
9
+ spec.authors = ["Jkr2255"]
10
+ spec.email = ["magnesium.oxide.play@gmail.com"]
11
+
12
+ spec.summary = %q{Calculate bit count (popcount, Hamming weight) faster in many environments.}
13
+ spec.homepage = "https://github.com/jkr2255/bit_counter"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+ if RUBY_PLATFORM =~ /java/
21
+ spec.platform = 'java'
22
+ else
23
+ spec.extensions = ["ext/bit_counter/extconf.rb"]
24
+ end
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.11"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "rake-compiler"
29
+ spec.add_development_dependency "rspec", "~> 3.0"
30
+ end
@@ -0,0 +1,14 @@
1
+ #ifndef BIG_PACK_H
2
+ #define BIG_PACK_H 1
3
+
4
+ #ifdef HAVE_RB_BIG_PACK
5
+ #define BIG_PACK(val, ptr, cnt) rb_big_pack(val, ptr, cnt)
6
+ #elif defined(HAVE_RB_INTEGER_PACK)
7
+ #define BIG_PACK(val, ptr, cnt) rb_integer_pack(val, ptr, cnt, sizeof(long), 0, \
8
+ INTEGER_PACK_LSWORD_FIRST|INTEGER_PACK_NATIVE_BYTE_ORDER| \
9
+ INTEGER_PACK_2COMP)
10
+ #else
11
+ #error This Ruby is not supported.
12
+ #endif
13
+
14
+ #endif
@@ -0,0 +1,18 @@
1
+ #ifndef BIGNUM_IN_ULONG_H
2
+ #define BIGNUM_IN_ULONG_H 1
3
+
4
+ #ifdef HAVE_RB_ABSINT_NUMWORDS
5
+ #define BIGNUM_IN_ULONG(v) rb_absint_numwords(v, sizeof(unsigned long), NULL)
6
+ #else
7
+ static size_t BIGNUM_IN_ULONG(VALUE v){
8
+ static ID size_id = 0;
9
+ VALUE ret_val;
10
+ if(!size_id){
11
+ size_id = rb_intern("size");
12
+ }
13
+ ret_val = rb_funcall(v, size_id, 0);
14
+ return (NUM2SIZET(ret_val) + sizeof(unsigned long) - 1) / sizeof(unsigned long);
15
+ }
16
+ #endif
17
+
18
+ #endif
@@ -0,0 +1,172 @@
1
+ #include "bit_counter.h"
2
+
3
+ VALUE rb_mBitCounter;
4
+
5
+ static VALUE bitcounter_cimpl_count_fixnum(VALUE self, VALUE num){
6
+ long l_num = NUM2LONG(num);
7
+ int val = POPCOUNTL(l_num);
8
+ if(l_num < 0) val -= sizeof(long) * CHAR_BIT;
9
+ return INT2FIX(val);
10
+ }
11
+
12
+ #ifdef HAVE___GET_CPUID
13
+ static VALUE bitcounter_cimpl_cpu_popcnt_p(VALUE self){
14
+ unsigned int eax, ebx, ecx = 0, edx;
15
+ __get_cpuid(1, &eax, &ebx, &ecx, &edx);
16
+ return (ecx & bit_POPCNT) ? Qtrue : Qfalse;
17
+ }
18
+ #else
19
+ static VALUE bitcounter_cimpl_cpu_popcnt_p(VALUE self){
20
+ return Qfalse;
21
+ }
22
+ #endif
23
+
24
+ #ifdef HAVE_POPCNT_GCC_ASM
25
+ static VALUE bitcounter_cimpl_count_fixnum_asm(VALUE self, VALUE num){
26
+ long l_num = NUM2LONG(num);
27
+ long val;
28
+ __asm__ volatile ("POPCNT %1, %0;": "=r"(val): "r"(l_num) : );
29
+ if(l_num < 0) val -= sizeof(long) * CHAR_BIT;
30
+ return INT2FIX(val);
31
+ }
32
+ #define ASM_POPCOUNT 1
33
+ #else
34
+ static VALUE bitcounter_cimpl_count_fixnum_asm(VALUE self, VALUE num){
35
+ /* dummy function for C compiler, never called from Ruby */
36
+ return Qnil;
37
+ }
38
+ #define ASM_POPCOUNT 0
39
+ #endif
40
+
41
+ /* for bignum */
42
+ static VALUE bitcounter_cimpl_count_bignum(VALUE self, VALUE num){
43
+ int negated = 0;
44
+ unsigned long * packed;
45
+ VALUE abs_num;
46
+ size_t words, i;
47
+ LONG_LONG ret = 0;
48
+ if(FIXNUM_P(num)){
49
+ return bitcounter_cimpl_count_fixnum(self, num);
50
+ }
51
+ Check_Type(num, T_BIGNUM);
52
+ if(RBIGNUM_NEGATIVE_P(num)){
53
+ negated = 1;
54
+ abs_num = BIG_NEG(num);
55
+ }else{
56
+ abs_num = num;
57
+ }
58
+ words = BIGNUM_IN_ULONG(abs_num);
59
+ if(words < ALLOCA_THRESHOLD){
60
+ packed = ALLOCA_N(unsigned long, words);
61
+ }else{
62
+ packed = ALLOC_N(unsigned long, words);
63
+ }
64
+ BIG_PACK(abs_num, packed, words);
65
+ for(i = 0; i < words; ++i){
66
+ ret += POPCOUNTL(packed[i]);
67
+ }
68
+ if(negated) ret = -ret;
69
+ if(words >= ALLOCA_THRESHOLD) xfree(packed);
70
+ return LL2NUM(ret);
71
+ }
72
+
73
+ #if defined(HAVE_POPCNT_LL_GCC_ASM) && (SIZEOF_LONG_LONG == SIZEOF_LONG * 2)
74
+ /* for Windows */
75
+ static VALUE bitcounter_cimpl_count_bignum_asm(VALUE self, VALUE num){
76
+ int negated = 0;
77
+ unsigned long * packed;
78
+ unsigned long long * ull_packed;
79
+ unsigned long long ull_i, ull_o = 0;
80
+ VALUE abs_num;
81
+ size_t words, i, ull_words;
82
+ LONG_LONG ret = 0;
83
+ if(FIXNUM_P(num)){
84
+ return bitcounter_cimpl_count_fixnum(self, num);
85
+ }
86
+ Check_Type(num, T_BIGNUM);
87
+ if(RBIGNUM_NEGATIVE_P(num)){
88
+ negated = 1;
89
+ abs_num = BIG_NEG(num);
90
+ }else{
91
+ abs_num = num;
92
+ }
93
+ words = BIGNUM_IN_ULONG(abs_num);
94
+ if(words < ALLOCA_THRESHOLD){
95
+ packed = ALLOCA_N(unsigned long, words);
96
+ }else{
97
+ packed = ALLOC_N(unsigned long, words);
98
+ }
99
+ BIG_PACK(abs_num, packed, words);
100
+ ull_words = words / 2;
101
+ ull_packed = (unsigned long long *) packed;
102
+ for(i = 0; i < ull_words; ++i){
103
+ ull_i = ull_packed[i];
104
+ __asm__ volatile ("POPCNT %1, %0;": "=r"(ull_o): "r"(ull_i) : );
105
+ ret += ull_o;
106
+ }
107
+ if(words & 1) ret += POPCOUNTL(packed[words-1]);
108
+ if(negated) ret = -ret;
109
+ if(words >= ALLOCA_THRESHOLD) xfree(packed);
110
+ return LL2NUM(ret);
111
+ }
112
+
113
+ #elif defined(HAVE_POPCNT_GCC_ASM)
114
+ static VALUE bitcounter_cimpl_count_bignum_asm(VALUE self, VALUE num){
115
+ int negated = 0;
116
+ unsigned long * packed;
117
+ unsigned long ul_i, ul_o = 0;
118
+ VALUE abs_num;
119
+ size_t words, i;
120
+ LONG_LONG ret = 0;
121
+ if(FIXNUM_P(num)){
122
+ return bitcounter_cimpl_count_fixnum(self, num);
123
+ }
124
+ Check_Type(num, T_BIGNUM);
125
+ if(RBIGNUM_NEGATIVE_P(num)){
126
+ negated = 1;
127
+ abs_num = BIG_NEG(num);
128
+ }else{
129
+ abs_num = num;
130
+ }
131
+ words = BIGNUM_IN_ULONG(abs_num);
132
+ if(words < ALLOCA_THRESHOLD){
133
+ packed = ALLOCA_N(unsigned long, words);
134
+ }else{
135
+ packed = ALLOC_N(unsigned long, words);
136
+ }
137
+ BIG_PACK(abs_num, packed, words);
138
+ for(i = 0; i < words; ++i){
139
+ ul_i = packed[i];
140
+ __asm__ volatile ("POPCNT %1, %0;": "=r"(ul_o): "r"(ul_i) : );
141
+ ret += ul_o;
142
+ }
143
+ if(negated) ret = -ret;
144
+ if(words >= ALLOCA_THRESHOLD) xfree(packed);
145
+ return LL2NUM(ret);
146
+ }
147
+ #else
148
+ static VALUE bitcounter_cimpl_count_bignum_asm(VALUE self, VALUE num){
149
+ /* dummy function for C compiler, never called from Ruby */
150
+ return Qnil;
151
+ }
152
+
153
+ #endif
154
+
155
+
156
+ void
157
+ Init_bit_counter(void)
158
+ {
159
+ VALUE rb_mCImpl;
160
+ VALUE have_cpu_popcnt;
161
+ rb_mBitCounter = rb_define_module("BitCounter");
162
+ rb_mCImpl = rb_define_module_under(rb_mBitCounter, "CImpl");
163
+ have_cpu_popcnt = bitcounter_cimpl_cpu_popcnt_p(rb_mCImpl);
164
+ rb_define_module_function(rb_mCImpl, "cpu_popcnt?", bitcounter_cimpl_cpu_popcnt_p, 0);
165
+ if(ASM_POPCOUNT && have_cpu_popcnt){
166
+ rb_define_method(rb_mCImpl, "count_fixnum", bitcounter_cimpl_count_fixnum_asm, 1);
167
+ rb_define_method(rb_mCImpl, "count_bignum", bitcounter_cimpl_count_bignum_asm, 1);
168
+ }else{
169
+ rb_define_method(rb_mCImpl, "count_fixnum", bitcounter_cimpl_count_fixnum, 1);
170
+ rb_define_method(rb_mCImpl, "count_bignum", bitcounter_cimpl_count_bignum, 1);
171
+ }
172
+ }
@@ -0,0 +1,24 @@
1
+ #ifndef BIT_COUNTER_H
2
+ #define BIT_COUNTER_H 1
3
+
4
+ #include "ruby.h"
5
+
6
+ #include <limits.h>
7
+
8
+ #ifdef HAVE_CPUID_H
9
+ #include <cpuid.h>
10
+ #endif
11
+
12
+ #include "popcountl.h"
13
+ #include "bignum_in_ulong.h"
14
+ #include "big_pack.h"
15
+
16
+ #ifdef HAVE_RB_BIG_XOR
17
+ #define BIG_NEG(val) rb_big_xor(val, INT2FIX(-1))
18
+ #else
19
+ #define BIG_NEG(val) rb_funcall(val, rb_intern("~"), 0)
20
+ #endif
21
+
22
+ #define ALLOCA_THRESHOLD (8192 / sizeof(unsigned long))
23
+
24
+ #endif /* BIT_COUNTER_H */
@@ -0,0 +1,48 @@
1
+ require "mkmf"
2
+
3
+ have_func('__builtin_popcountl(1)')
4
+ have_func('rb_absint_numwords')
5
+ have_func('rb_big_pack')
6
+ have_func('rb_integer_pack')
7
+ have_func('rb_big_xor')
8
+ have_header('cpuid.h') && have_func('__get_cpuid', 'cpuid.h')
9
+ check_sizeof('long')
10
+ check_sizeof('long long')
11
+ message 'checking for usability of POPCNT in GCC-style inline assembler... '
12
+ gcc_popcount = try_compile <<SRC
13
+ int main(){
14
+ long a=1, b=2;
15
+ __asm__ volatile ("POPCNT %1, %0;"
16
+ :"=r"(b)
17
+ :"r"(a)
18
+ :
19
+ );
20
+ return 0;
21
+ }
22
+ SRC
23
+ if gcc_popcount
24
+ message "yes\n"
25
+ $defs << '-DHAVE_POPCNT_GCC_ASM'
26
+ message 'checking for usability of POPCNT for long long in GCC-style inline assembler... '
27
+ gcc_ll_popcount = try_compile <<SRC
28
+ int main(){
29
+ long long a=1, b=2;
30
+ __asm__ volatile ("POPCNT %1, %0;"
31
+ :"=r"(b)
32
+ :"r"(a)
33
+ :
34
+ );
35
+ return 0;
36
+ }
37
+ SRC
38
+ if gcc_ll_popcount
39
+ message "yes\n"
40
+ $defs << '-DHAVE_POPCNT_LL_GCC_ASM'
41
+ else
42
+ message "no\n"
43
+ end
44
+ else
45
+ message "no\n"
46
+ end
47
+
48
+ create_makefile("bit_counter/bit_counter")
@@ -0,0 +1,48 @@
1
+ /* header for long popcount (without CPU-native) */
2
+
3
+ #ifndef POPCOUNTL_H
4
+ #define POPCOUNTL_H 1
5
+
6
+ #ifdef HAVE___BUILTIN_POPCOUNTL
7
+ #define POPCOUNTL(x) __builtin_popcountl(x)
8
+ #elif SIZEOF_LONG == 8
9
+ static inline int POPCOUNTL(unsigned long x){
10
+ x = ((x & 0xaaaaaaaaaaaaaaaaUL) >> 1)
11
+ + (x & 0x5555555555555555UL);
12
+ x = ((x & 0xccccccccccccccccUL) >> 2)
13
+ + (x & 0x3333333333333333UL);
14
+ x = ((x & 0xf0f0f0f0f0f0f0f0UL) >> 4)
15
+ + (x & 0x0f0f0f0f0f0f0f0fUL);
16
+ x = ((x & 0xff00ff00ff00ff00UL) >> 8)
17
+ + (x & 0x00ff00ff00ff00ffUL);
18
+ x = ((x & 0xffff0000ffff0000UL) >> 16)
19
+ + (x & 0x0000ffff0000ffffUL);
20
+ x = ((x & 0xffffffff00000000UL) >> 32)
21
+ + (x & 0x00000000ffffffffUL);
22
+ return (int) x;
23
+ }
24
+ #elif SIZEOF_LONG == 4
25
+ static inline int POPCOUNTL(unsigned long x){
26
+ x = ((x & 0xaaaaaaaaUL) >> 1)
27
+ + (x & 0x55555555UL);
28
+ x = ((x & 0xccccccccUL) >> 2)
29
+ + (x & 0x33333333UL);
30
+ x = ((x & 0xf0f0f0f0UL) >> 4)
31
+ + (x & 0x0f0f0f0fUL);
32
+ x = ((x & 0xff00ff00UL) >> 8)
33
+ + (x & 0x00ff00ffUL);
34
+ x = ((x & 0xffff0000UL) >> 16)
35
+ + (x & 0x0000ffffUL);
36
+ return (int) x;
37
+ }
38
+ #else
39
+ #error Unsupported architecture
40
+ #endif
41
+
42
+ #if SIZEOF_LONG == 8
43
+ #define BER_MASK 0x7f7f7f7f7f7f7f7fUL
44
+ #elif SIZEOF_LONG == 4
45
+ #define BER_MASK 0x7f7f7f7fUL
46
+ #endif
47
+
48
+ #endif
@@ -0,0 +1,21 @@
1
+ require 'bit_counter'
2
+
3
+ # :nodoc:
4
+ class Fixnum
5
+ #
6
+ # calling BitCounter.#count with self
7
+ #
8
+ def bit_count
9
+ BitCounter.count_fixnum(self)
10
+ end
11
+ end
12
+
13
+ # :nodoc:
14
+ class Bignum
15
+ #
16
+ # calling BitCounter.#count with self
17
+ #
18
+ def bit_count
19
+ BitCounter.count_bignum(self)
20
+ end
21
+ end
@@ -0,0 +1,8 @@
1
+ #
2
+ # module for bit counting
3
+ #
4
+ module BitCounter
5
+
6
+ extend CImpl
7
+
8
+ end
@@ -0,0 +1,28 @@
1
+ require 'java'
2
+
3
+ #
4
+ # module for bit counting
5
+ #
6
+ module BitCounter
7
+
8
+ #
9
+ # module for Java-specific codes of BitCounter.
10
+ # @note not intended for direct use.
11
+ #
12
+ module JavaImpl
13
+ def count_fixnum(num)
14
+ raise TypeError unless num.is_a?(::Fixnum)
15
+ count = Java::JavaLang::Long.bitCount(num)
16
+ num >= 0 ? count : count - 64
17
+ end
18
+
19
+ def count_bignum(num)
20
+ raise TypeError unless num.is_a?(::Bignum)
21
+ count = num.to_java.bitCount
22
+ num >= 0 ? count : -count
23
+ end
24
+ end
25
+
26
+ extend JavaImpl
27
+
28
+ end
@@ -0,0 +1,3 @@
1
+ module BitCounter
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,34 @@
1
+ require "bit_counter/version"
2
+
3
+ if RUBY_PLATFORM =~ /java/
4
+ require 'bit_counter/jruby'
5
+ else
6
+ require "bit_counter/bit_counter"
7
+ require 'bit_counter/cruby'
8
+ end
9
+
10
+ #
11
+ # module for bit counting
12
+ #
13
+ module BitCounter
14
+
15
+ module_function
16
+
17
+ #
18
+ # counts bits in Integer.
19
+ # @return [Integer] num the number of bits.
20
+ # if positive number is given, count 1 bits.
21
+ # if negative number is given, count 0 bits and -(count) is returned.
22
+ # @raise [TypeError] when non-Integer was given.
23
+ #
24
+ def count(num)
25
+ case num
26
+ when Fixnum
27
+ count_fixnum(num)
28
+ when Bignum
29
+ count_bignum(num)
30
+ else
31
+ raise TypeError
32
+ end
33
+ end
34
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: bit_counter
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Jkr2255
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-04-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.11'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.11'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake-compiler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description:
70
+ email:
71
+ - magnesium.oxide.play@gmail.com
72
+ executables: []
73
+ extensions:
74
+ - ext/bit_counter/extconf.rb
75
+ extra_rdoc_files: []
76
+ files:
77
+ - ".gitignore"
78
+ - ".rspec"
79
+ - ".travis.yml"
80
+ - Gemfile
81
+ - LICENSE.txt
82
+ - README.md
83
+ - Rakefile
84
+ - benchmark/bignum.rb
85
+ - benchmark/fixnum.rb
86
+ - bin/console
87
+ - bin/setup
88
+ - bit_counter.gemspec
89
+ - ext/bit_counter/big_pack.h
90
+ - ext/bit_counter/bignum_in_ulong.h
91
+ - ext/bit_counter/bit_counter.c
92
+ - ext/bit_counter/bit_counter.h
93
+ - ext/bit_counter/extconf.rb
94
+ - ext/bit_counter/popcountl.h
95
+ - lib/bit_counter.rb
96
+ - lib/bit_counter/core_ext.rb
97
+ - lib/bit_counter/cruby.rb
98
+ - lib/bit_counter/jruby.rb
99
+ - lib/bit_counter/version.rb
100
+ homepage: https://github.com/jkr2255/bit_counter
101
+ licenses:
102
+ - MIT
103
+ metadata: {}
104
+ post_install_message:
105
+ rdoc_options: []
106
+ require_paths:
107
+ - lib
108
+ required_ruby_version: !ruby/object:Gem::Requirement
109
+ requirements:
110
+ - - ">="
111
+ - !ruby/object:Gem::Version
112
+ version: '0'
113
+ required_rubygems_version: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 2.5.1
121
+ signing_key:
122
+ specification_version: 4
123
+ summary: Calculate bit count (popcount, Hamming weight) faster in many environments.
124
+ test_files: []