File Coverage

blib/lib/Set/Similarity/BV.pm
Criterion Covered Total %
statement 64 64 100.0
branch 10 10 100.0
condition 10 12 91.6
subroutine 17 17 100.0
pod 7 7 100.0
total 108 110 99.0


line stmt bran cond sub pod time code
1             package Set::Similarity::BV;
2              
3 5     5   2178 use strict;
  5         7  
  5         129  
4 5     5   17 use warnings;
  5         7  
  5         179  
5              
6             our $VERSION = '0.06';
7              
8 5     5   30 use Carp 'croak';
  5         6  
  5         576  
9              
10              
11             our $width = int 0.999+log(~0)/log(2);
12             our $chunk_size = int($width/4);
13              
14             # https://graphics.stanford.edu/~seander/bithacks.html#CountBitsSetParallel
15             our $final_shift = (($width/8) - 1) * 8;
16 5     5   26 no warnings 'portable'; # for 0xffffffffffffffff
  5         12  
  5         1001  
17             our $x01 = hex(substr('01'x16,0,$chunk_size));
18             our $x33 = hex(substr('33'x16,0,$chunk_size));
19             our $x55 = hex(substr('55'x16,0,$chunk_size));
20             our $x0f = hex(substr('0f'x16,0,$chunk_size));
21              
22              
23             sub new {
24 7     7 1 2135 my $class = shift;
25              
26             # uncoverable condition false
27 7 100 66     50 bless @_ ? @_ > 1 ? {@_} : {%{$_[0]}} : {}, ref $class || $class;
  2 100       12  
28             }
29              
30             sub similarity {
31 44     44 1 5872 my ($self, $hex1, $hex2) = @_;
32              
33 44 100 100     190 return 1 if (!($hex1 || $hex2)); # both zero
34 36 100 100     207 return 0 unless ($hex1 && $hex2); # one is zero
35              
36             #return 1 if (!(hex($hex1) || hex($hex2))); # both zero
37             #return 0 unless (hex($hex1) && hex($hex2) ); # one is zero
38              
39 5     5   25 no warnings 'portable'; # for 0xffffffffffffffff
  5         8  
  5         526  
40              
41 24         67 return $self->from_integers(
42             $self->_integers($hex1),
43             $self->_integers($hex2),
44             );
45             }
46              
47              
48 1     1 1 447 sub from_integers { croak 'Method "from_integers" not implemented in subclass' }
49              
50             sub intersection {
51 30     30 1 37 my ($self,$v1,$v2) = @_;
52              
53 5     5   21 no warnings 'portable'; # for 0xffffffffffffffff
  5         6  
  5         741  
54              
55 30         28 my $bits = 0;
56 30         30 my $max1 = scalar(@{$v1}) - 1;
  30         43  
57 30         26 my $max2 = scalar(@{$v2}) - 1;
  30         30  
58              
59 30   66     150 for (my $i=0; ($i <= $max1) && ($i <= $max2); $i++) {
60 33         105 $bits += $self->bits([ ($v1->[$i] & $v2->[$i]) ]);
61             }
62 30         69 return $bits;
63             }
64              
65             sub _integers {
66 62     62   127 my ($self,$hex_string) = @_;
67              
68 62         458 my @chunks = $hex_string =~ m/([0-9a-f]{1,$chunk_size})/gi;
69              
70 5     5   24 no warnings 'portable'; # for 0xffffffffffffffff
  5         10  
  5         465  
71 62         86 my $result = [];
72 62         91 for my $chunk (@chunks) {
73 71         57 push @{$result},hex($chunk);
  71         195  
74             }
75 62         215 return $result;
76             }
77              
78             sub bits {
79 87     87 1 83 my ($self,$array_of_integers) = @_;
80              
81 5     5   2521 use integer;
  5         46  
  5         56  
82 5     5   174 no warnings 'portable'; # for 0xffffffffffffffff
  5         7  
  5         906  
83              
84 87         72 my $bits = 0;
85              
86 87         65 for my $i (@{$array_of_integers}) {
  87         110  
87 95         67 my $v = $i; # don't use (and change) $i directly
88 95         116 $v = $v - (($v >> 1) & $x55);
89 95         93 $v = ($v & $x33) + (($v >> 2) & $x33);
90             # (bytesof($v) -1) * bitsofbyte = (8-1)*8 = --vvvvvvv
91 95         92 $v = (($v + ($v >> 4) & $x0f) * $x01) >> $final_shift;
92 95         102 $bits += $v;
93             }
94              
95 87         261 return $bits;
96             }
97              
98             sub combined_length {
99 10     10 1 12 my ($self,$v1,$v2) = @_;
100              
101 10         15 return $self->bits($v1) + $self->bits($v2);
102             }
103              
104             sub min {
105 8 100   8 1 70 ($_[1] < $_[2])
106             ? $_[1] : $_[2];
107             }
108              
109             1;
110              
111             __END__