File Coverage

blib/lib/CracTools/BitVector.pm
Criterion Covered Total %
statement 105 127 82.6
branch 21 38 55.2
condition 14 72 19.4
subroutine 19 19 100.0
pod 15 15 100.0
total 174 271 64.2


line stmt bran cond sub pod time code
1             package CracTools::BitVector;
2             {
3             $CracTools::BitVector::DIST = 'CracTools';
4             }
5             # ABSTRACT: Full Perl BitVector implementation
6             $CracTools::BitVector::VERSION = '1.22';
7 2     2   20772 use Exporter;
  2         4  
  2         114  
8             our @ISA = qw(Exporter);
9              
10 2     2   10 use strict;
  2         4  
  2         46  
11 2     2   1530 use POSIX qw(floor ceil);
  2         15779  
  2         12  
12 2     2   2822 use constant BITNESS => 64;
  2         4  
  2         2903  
13              
14              
15             sub new {
16 51     51 1 89 my ($class, $length) = @_;
17 51         84 my $this = {};
18 51         72 bless($this, $class);
19            
20 51         132 $this->{n} = $length;
21 51         75 $this->{set} = 0;
22 51         92 $this->{first_bit_set} = $length + 1;
23              
24 51         79 my $max = int($length / BITNESS);
25 51 50       180 if ($length % BITNESS > 0) {
26 51         61 $max++;
27             }
28 51         72 $this->{maxCases} = $max;
29              
30 51         108 for (my $i=0; $i < $max; $i++) {
31 2481         2428 push @{$this->{bits}}, 0;
  2481         6469  
32             }
33              
34 51         160 return $this;
35             }
36              
37              
38             sub firstBitSet {
39 3     3 1 5 my $self = shift;
40 3         9 return $self->{first_bit_set};
41             }
42              
43              
44             sub copy {
45 1     1 1 2 my ($this) = @_;
46 1         3 my $new = {};
47 1         3 $new->{n} = $this->{n};
48 1         2 $new->{set} = $this->{set};
49 1         3 $new->{maxCases} = $this->{maxCases};
50 1         2 @{$new->{bits}} = @{$this->{bits}};
  1         3  
  1         3  
51 1 50       3 if (defined($this->{block})) {
52 1         2 @{$new->{block}} = @{$this->{block}};
  1         10  
  1         2  
53             }
54 1         3 bless $new, ref($this);
55 1         3 return $new;
56             }
57              
58              
59             sub set {
60 10     10 1 23 my ($this, $i) = @_;
61 10 100       30 $this->{first_bit_set} = $i if $i < $this->{first_bit_set};
62 10 50       24 if (! $this->get($i)) {
63 10         15 $this->{set}++;
64 10         22 $this->{bits}[int($i / BITNESS)] |= (1 << ($i % BITNESS));
65 10         39 $this->{block}[int($i / BITNESS)]++;
66             }
67             }
68              
69              
70             sub unset {
71 1     1 1 3 my ($this, $i) = @_;
72 1 50       3 if ($this->get($i)) {
73 1         2 $this->{set}--;
74 1         5 $this->{bits}[int($i / BITNESS)] &= (~(1 << ($i % BITNESS)));
75 1         3 $this->{block}[int($i / BITNESS)]--;
76             }
77             }
78              
79              
80             sub get {
81 107     107 1 166 my ($this, $i) = @_;
82 107 50 33     534 if (! defined($this) || ! defined($this->{bits})
      33        
83             || ! defined($this->{bits}[int($i / BITNESS)])) {
84 0         0 die("Bad position i=$i case -> ".int($i/BITNESS)."\n");
85             }
86 107 100       585 return ($this->{bits}[int($i / BITNESS)] & (1 << ($i % BITNESS)))? 1 : 0;
87             }
88              
89              
90             sub prev {
91 9     9 1 16 my ($this, $i, $max) = @_;
92 9         13 my $start = $i;
93            
94             # Is there a 1 in the current block?
95 9 100       34 if ($this->{bits}[int($i / BITNESS)]
96             & ((~0) >> (BITNESS-1) - ($i % BITNESS))) {
97 5         12 while ($this->get($i) == 0) {
98 12         26 $i--;
99             }
100 5 50 33     19 if (defined($max) && $i < $start - $max) {
101 0         0 return -1;
102             }
103 5         13 return $i;
104             } else {
105             # Look for a block with a 1-bit set.
106              
107 4         8 my $block = int($i / BITNESS) - 1;
108 4   0     15 while ($block >= 0
      33        
      0        
      33        
109             && (! defined($this->{block}[$block])
110             || ! $this->{block}[$block])
111             && (! defined($max)
112             || $i - ($block+1) * (BITNESS) + 1 <= $max)) {
113 0         0 $block--;
114             }
115 4 50 0     17 if ($block < 0 || (defined($max)
      33        
116             && $i - ($block+1) * (BITNESS) + 1 > $max)) {
117 4         8 return -1;
118             }
119 0         0 $i = ($block + 1) * (BITNESS) - 1;
120 0         0 while ($this->get($i) == 0) {
121 0         0 $i--;
122             }
123 0 0 0     0 if (defined($max) && $i < $start - $max) {
124 0         0 return -1;
125             }
126 0         0 return $i;
127             }
128             }
129              
130              
131             sub succ {
132 3     3 1 7 my ($this, $i, $max) = @_;
133 3         5 my $start = $i;
134              
135             # Is there a 1 in the current block?
136 3 50       15 if ($this->{bits}[int($i / BITNESS)]
137             & ((~0) << ($i % BITNESS))) {
138 3   66     17 while ($i < $this->{n} && $this->get($i) == 0) {
139 11         36 $i++;
140             }
141 3 50 33     29 if ($i == $this->{n} || (defined($max) && $i > $max + $start)) {
      33        
142 0         0 return -1;
143             }
144 3         9 return $i;
145             } else {
146             # Look for a block with a 1-bit set.
147              
148 0         0 my $block = int($i / BITNESS) + 1;
149 0   0     0 while ($block < $this->{maxCases}
      0        
      0        
      0        
150             && (! defined($this->{block}[$block])
151             || ! $this->{block}[$block])
152             && (! defined($max)
153             || $block * (BITNESS) - $i <= $max)) {
154 0         0 $block++;
155             }
156 0 0 0     0 if ($block >= $this->{maxCases}
      0        
157             || (defined($max)
158             && $block * (BITNESS) - $i > $max)) {
159 0         0 return -1;
160             }
161 0         0 $i = $block * (BITNESS);
162 0   0     0 while ($i < $this->{n} && $this->get($i) == 0) {
163 0         0 $i++;
164             }
165 0 0 0     0 if ($i == $this->{n} || defined($max) && $i > $max + $start) {
      0        
166 0         0 return -1;
167             }
168 0         0 return $i;
169             }
170             }
171              
172              
173             sub rank {
174 4     4 1 8 my ($self, $i) = @_;
175 4 100       11 my $rank = $self->get($i)? 1 : 0;
176 4         6 my $found_bit = 1;
177 4         11 while($found_bit) {
178 8         27 $i = $self->prev($i-1);
179 8 100       18 if ($i != -1) {
180 4         11 $rank++;
181             } else {
182 4         11 $found_bit = 0;
183             }
184             }
185 4         16 return $rank;
186             }
187              
188              
189             sub select {
190 2     2 1 4 my ($self, $nb) = @_;
191 2         7 my $i = $self->firstBitSet;
192 2   66     17 while ($nb > 1 && $i != -1) {
193 2         12 $i = $self->succ($i+1);
194 2         8 $nb--;
195             }
196 2         7 return $i;
197             }
198              
199              
200             sub length {
201 4     4 1 12 my ($this) = @_;
202 4         22 return $this->{n};
203             }
204              
205              
206             sub nbSet {
207 9     9 1 15 my ($this) = @_;
208 9         39 return $this->{set};
209             }
210              
211             # Retro-compatibility alias
212 9     9 1 14 sub nb_set { my $self = shift; $self->nbSet(@_);}
  9         22  
213              
214              
215             sub toString {
216 4     4 1 8 my $this = shift;
217 4         6 my $sep = shift;
218 4         8 my $output = '';
219              
220 4 50       28 $sep = ' ' unless defined $sep;
221            
222 4         15 for (my $i=0; $i < $this->{n}; $i++) {
223 40 50 66     109 if ($i % BITNESS == 0 && $i > 0) {
224 0         0 $output .= $sep;
225             }
226 40         83 $output .= $this->get($i);
227             }
228 4         19 return $output
229             }
230              
231             # Retro-compatibility alias
232 4     4 1 11 sub to_string { my $self = shift; $self->toString(@_);}
  4         12  
233              
234             1;
235              
236             __END__