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.25';
7 2     2   12754 use Exporter;
  2         2  
  2         92  
8             our @ISA = qw(Exporter);
9              
10 2     2   6 use strict;
  2         7  
  2         35  
11 2     2   847 use POSIX qw(floor ceil);
  2         10072  
  2         8  
12 2     2   1625 use constant BITNESS => 64;
  2         2  
  2         1901  
13              
14              
15             sub new {
16 51     51 1 59 my ($class, $length) = @_;
17 51         47 my $this = {};
18 51         35 bless($this, $class);
19            
20 51         67 $this->{n} = $length;
21 51         35 $this->{set} = 0;
22 51         53 $this->{first_bit_set} = $length + 1;
23              
24 51         50 my $max = int($length / BITNESS);
25 51 50       67 if ($length % BITNESS > 0) {
26 51         36 $max++;
27             }
28 51         33 $this->{maxCases} = $max;
29              
30 51         69 for (my $i=0; $i < $max; $i++) {
31 2481         1265 push @{$this->{bits}}, 0;
  2481         3238  
32             }
33              
34 51         91 return $this;
35             }
36              
37              
38             sub firstBitSet {
39 3     3 1 6 my $self = shift;
40 3         8 return $self->{first_bit_set};
41             }
42              
43              
44             sub copy {
45 1     1 1 3 my ($this) = @_;
46 1         2 my $new = {};
47 1         2 $new->{n} = $this->{n};
48 1         1 $new->{set} = $this->{set};
49 1         2 $new->{maxCases} = $this->{maxCases};
50 1         2 @{$new->{bits}} = @{$this->{bits}};
  1         4  
  1         2  
51 1 50       4 if (defined($this->{block})) {
52 1         1 @{$new->{block}} = @{$this->{block}};
  1         2  
  1         2  
53             }
54 1         2 bless $new, ref($this);
55 1         3 return $new;
56             }
57              
58              
59             sub set {
60 10     10 1 13 my ($this, $i) = @_;
61 10 100       22 $this->{first_bit_set} = $i if $i < $this->{first_bit_set};
62 10 50       17 if (! $this->get($i)) {
63 10         7 $this->{set}++;
64 10         15 $this->{bits}[int($i / BITNESS)] |= (1 << ($i % BITNESS));
65 10         23 $this->{block}[int($i / BITNESS)]++;
66             }
67             }
68              
69              
70             sub unset {
71 1     1 1 2 my ($this, $i) = @_;
72 1 50       3 if ($this->get($i)) {
73 1         2 $this->{set}--;
74 1         3 $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 80 my ($this, $i) = @_;
82 107 50 33     362 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       292 return ($this->{bits}[int($i / BITNESS)] & (1 << ($i % BITNESS)))? 1 : 0;
87             }
88              
89              
90             sub prev {
91 9     9 1 10 my ($this, $i, $max) = @_;
92 9         7 my $start = $i;
93            
94             # Is there a 1 in the current block?
95 9 100       31 if ($this->{bits}[int($i / BITNESS)]
96             & ((~0) >> (BITNESS-1) - ($i % BITNESS))) {
97 5         11 while ($this->get($i) == 0) {
98 12         14 $i--;
99             }
100 5 50 33     13 if (defined($max) && $i < $start - $max) {
101 0         0 return -1;
102             }
103 5         10 return $i;
104             } else {
105             # Look for a block with a 1-bit set.
106              
107 4         6 my $block = int($i / BITNESS) - 1;
108 4   0     12 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     12 if ($block < 0 || (defined($max)
      33        
116             && $i - ($block+1) * (BITNESS) + 1 > $max)) {
117 4         6 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 4 my ($this, $i, $max) = @_;
133 3         3 my $start = $i;
134              
135             # Is there a 1 in the current block?
136 3 50       14 if ($this->{bits}[int($i / BITNESS)]
137             & ((~0) << ($i % BITNESS))) {
138 3   66     11 while ($i < $this->{n} && $this->get($i) == 0) {
139 11         20 $i++;
140             }
141 3 50 33     20 if ($i == $this->{n} || (defined($max) && $i > $max + $start)) {
      33        
142 0         0 return -1;
143             }
144 3         7 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 4 my ($self, $i) = @_;
175 4 100       8 my $rank = $self->get($i)? 1 : 0;
176 4         3 my $found_bit = 1;
177 4         11 while($found_bit) {
178 8         12 $i = $self->prev($i-1);
179 8 100       14 if ($i != -1) {
180 4         6 $rank++;
181             } else {
182 4         8 $found_bit = 0;
183             }
184             }
185 4         32 return $rank;
186             }
187              
188              
189             sub select {
190 2     2 1 4 my ($self, $nb) = @_;
191 2         4 my $i = $self->firstBitSet;
192 2   66     13 while ($nb > 1 && $i != -1) {
193 2         10 $i = $self->succ($i+1);
194 2         4 $nb--;
195             }
196 2         6 return $i;
197             }
198              
199              
200             sub length {
201 4     4 1 8 my ($this) = @_;
202 4         15 return $this->{n};
203             }
204              
205              
206             sub nbSet {
207 9     9 1 9 my ($this) = @_;
208 9         23 return $this->{set};
209             }
210              
211             # Retro-compatibility alias
212 9     9 1 8 sub nb_set { my $self = shift; $self->nbSet(@_);}
  9         16  
213              
214              
215             sub toString {
216 4     4 1 1 my $this = shift;
217 4         6 my $sep = shift;
218 4         3 my $output = '';
219              
220 4 50       21 $sep = ' ' unless defined $sep;
221            
222 4         9 for (my $i=0; $i < $this->{n}; $i++) {
223 40 50 66     66 if ($i % BITNESS == 0 && $i > 0) {
224 0         0 $output .= $sep;
225             }
226 40         38 $output .= $this->get($i);
227             }
228 4         12 return $output
229             }
230              
231             # Retro-compatibility alias
232 4     4 1 6 sub to_string { my $self = shift; $self->toString(@_);}
  4         11  
233              
234             1;
235              
236             __END__