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