File Coverage

blib/lib/Test/Probability.pm
Criterion Covered Total %
statement 39 39 100.0
branch 5 8 62.5
condition n/a
subroutine 11 11 100.0
pod 1 3 33.3
total 56 61 91.8


line stmt bran cond sub pod time code
1             package Test::Probability;
2             {
3             $Test::Probability::VERSION = '0.002';
4             }
5              
6 5     5   140572 use strict;
  5         11  
  5         193  
7 5     5   26 use warnings;
  5         9  
  5         391  
8              
9             require Exporter;
10             our @ISA = qw/Exporter/;
11             our @EXPORT = qw/dist_ok/;
12              
13             require AutoLoader; # Statistics::Distributions should do this, but doesn't.
14 5     5   5015 use Statistics::Distributions 1.02 qw/chisqrdistr/;
  5         18832  
  5         415  
15 5     5   39 use Test::More;
  5         10  
  5         35  
16 5     5   1715 use List::Util qw/sum/;
  5         8  
  5         605  
17 5     5   5346 use List::MoreUtils qw/pairwise/;
  5         6589  
  5         497  
18 5     5   36 use Carp;
  5         10  
  5         1992  
19              
20             sub chisq {
21 2     2 0 583 my ($observed, $expected) = @_;
22             return sum(pairwise {
23 31 100   31   59 my $denom = $b > 0.5 ? $b : 0.5;
24 31         77 ($a - $b)**2 / $denom;
25 2         29 } @$observed, @$expected);
26             }
27              
28             sub fits_distribution {
29 2     2 0 6 my ($observed, $dist, $confidence) = @_;
30 2 50       16 croak "First arg not an array or arrayref" unless ref $observed eq 'ARRAY';
31 2 50       9 croak "Second arg not an array or arrayref" unless ref $dist eq 'ARRAY';
32 2 50       9 croak "Size of observed domain does not match size of distribution domain"
33             unless scalar(@$observed) == scalar(@$dist);
34 2         19 my $observations = sum @$observed;
35 2         7 my $scalefactor = $observations / (sum @$dist);
36 2         5 my @expected = map { $_ * $scalefactor } @$dist;
  31         49  
37 2         7 my $chisq = chisq($observed, \@expected);
38 2         11 my $dof = @$dist - 1; # degrees of freedom
39 2         11 my $threshold = chisqrdistr($dof, 1 - $confidence);
40 2         393 return $chisq <= $threshold;
41             }
42              
43             sub dist_ok {
44 2     2 1 981 my ($observed, $dist, $confidence, $message) = @_;
45 2         6 ok(fits_distribution($observed, $dist, $confidence), $message);
46             }
47              
48             1;
49              
50             __END__
51              
52             =head1 NAME
53              
54             Test::Probability - test if results are distributed correctly
55              
56             =head1 SYNOPSIS
57              
58             use Test::Probability;
59              
60             my @results;
61             for (0 .. 1000) {
62             $results[rand_fn()]++;
63             }
64             my @probabilities = (0.5, 0.2, 0.3);
65             my $confidence = 0.9;
66             dist_ok(@results, @probabilities, $confidence,
67             "results match expected with confidence $confidence");
68              
69             =head1 DESCRIPTION
70              
71             Does your random-number generating function output the distribution you expect?
72             Now you can find out! This module performs a chi-squared test at the specified
73             confidence interval.
74              
75             =head1 EXPORTS
76              
77             =over
78              
79             =item dist_ok
80              
81             dist_ok(@results, @probabilities, $confidence,
82             "results match probabilities with confidence $confidence");
83              
84             =back
85            
86             =head1 AUTHOR
87              
88             Miles Gould, E<lt>mgould@cpan.orgE<gt>
89              
90             =head1 CONTRIBUTING
91              
92             This module is currently distributed along with Games::Dice::Loaded.
93             Please fork
94             L<the GitHub repository|http://github.com/pozorvlak/Games-Dice-Loaded>.
95              
96             =head1 COPYRIGHT AND LICENSE
97              
98             Copyright (C) 2011 by Miles Gould
99              
100             This library is free software; you can redistribute it and/or modify
101             it under the same terms as Perl itself, either Perl version 5.12.4 or,
102             at your option, any later version of Perl 5 you may have available.
103              
104             =head1 SEE ALSO
105              
106             =over
107              
108             =item L<Statistics::ChiSquared> (only handles even distributions).
109              
110             =item L<Statistics::Distributions> (used internally by this module).
111              
112             =back
113              
114             =cut