File Coverage

blib/lib/Statistics/Benford.pm
Criterion Covered Total %
statement 62 62 100.0
branch 15 20 75.0
condition 4 4 100.0
subroutine 11 11 100.0
pod 4 4 100.0
total 96 101 95.0


line stmt bran cond sub pod time code
1             package Statistics::Benford;
2              
3 5     5   135965 use strict;
  5         12  
  5         197  
4 5     5   28 use warnings;
  5         9  
  5         154  
5 5     5   27 use List::Util qw(sum);
  5         22  
  5         1001  
6              
7             our $VERSION = '0.08';
8             $VERSION = eval $VERSION;
9              
10 5     5   27 use constant _BASE => 0;
  5         13  
  5         442  
11 5     5   34 use constant _N => 1;
  5         9  
  5         259  
12 5     5   25 use constant _LEN => 2;
  5         9  
  5         296  
13 5     5   24 use constant _DIST => 3;
  5         8  
  5         3362  
14              
15             sub new {
16 14     14 1 8272 my ($class, $base, $n, $len) = @_;
17              
18 14   100     62 $base ||= 10;
19 14 100       38 $n = 0 unless defined $n;
20 14   100     51 $len ||= 1;
21              
22 14         21 my ($k_start, $k_end, $d_start);
23 14 100       36 if (0 == $n) {
24 11         21 ($k_start, $k_end) = (0, 0);
25 11         25 $d_start = $base ** ($len - 1);
26             }
27             else {
28 3         14 ($k_start, $k_end) = ($base ** ($n - 1), $base ** $n - 1);
29 3 50       12 $d_start = (1 == $len) ? 0 : $base ** ($len - 1);
30             }
31 14         55 my $d_end = $base ** $len - 1;
32              
33 14         19 my %dist;
34 14         34 for my $digit ($d_start .. $d_end) {
35 368         371 my $sum = 0;
36 368         425 for my $k ($k_start .. $k_end) {
37 608         1191 $sum += log(1 + 1 / ($k * $base + $digit));
38             }
39 368         3194 $dist{$digit} = (1 / log($base)) * $sum;
40             }
41              
42 14         90 return bless [$base, $n, $len, \%dist], $class;
43             }
44              
45             sub distribution {
46 6     6 1 265 return %{ $_[0]->[_DIST] };
  6         91  
47             }
48              
49             *dist = \&distribution;
50              
51             sub difference {
52 6     6 1 3592 my ($self, %freq) = @_;
53 6         10 my ($diff, %diff) = 0;
54              
55 6         40 my $count = sum values %freq;
56 6 50       12 return 0 unless $count;
57              
58 6         8 while (my ($num, $percent) = each %{ $self->[_DIST] }) {
  224         492  
59 218 50       369 my $delta = ($freq{$num} ? $freq{$num} / $count : 0) - $percent;
60 218         382 $diff += abs($diff{$num} = $delta);
61             }
62              
63 6 100       169 return wantarray ? %diff : $diff;
64             }
65              
66             *diff = \&difference;
67              
68             sub signif {
69 6     6 1 2615 my ($self, %freq) = @_;
70 6         12 my ($diff, %diff) = 0;
71              
72 6         42 my $count = sum values %freq;
73 6 50       15 return 0 unless $count;
74              
75 6         9 while (my ($num, $percent) = each %{ $self->[_DIST] }) {
  224         724  
76 218 50       409 my $delta = ($freq{$num} ? $freq{$num} / $count : 0) - $percent;
77 218 100       402 my $fix = abs $delta > (1 / (2 * $count)) ? (1 / (2 * $count)) : 0;
78 218         321 my $z = (abs($delta) - $fix) /
79             sqrt($percent * (1 - $percent) / $count);
80 218         458 $diff += $diff{ $num } = $z ;
81             }
82              
83 6 100       89 return wantarray ? %diff : $diff / keys %{ $self->[_DIST] };
  3         72  
84             }
85              
86             *z = \&signif;
87              
88             1;
89              
90             __END__