|  line  | 
 stmt  | 
 bran  | 
 cond  | 
 sub  | 
 pod  | 
 time  | 
 code  | 
| 
1
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 package Games::Dice::Probability;  | 
| 
2
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
3
 | 
29
 | 
 
 | 
 
 | 
  
29
  
 | 
 
 | 
669610
 | 
 use 5.006;  | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
196
 | 
    | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1316
 | 
    | 
| 
4
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
5
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Be a Good Module  | 
| 
6
 | 
29
 | 
 
 | 
 
 | 
  
29
  
 | 
 
 | 
179
 | 
 use strict;  | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
61
 | 
    | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
990
 | 
    | 
| 
7
 | 
29
 | 
 
 | 
 
 | 
  
29
  
 | 
 
 | 
805
 | 
 use warnings;  | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
64
 | 
    | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1614
 | 
    | 
| 
8
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #use diagnostics;  | 
| 
9
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
10
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Our version number.  | 
| 
11
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 our $VERSION = '0.02';  | 
| 
12
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
13
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Required Modules  | 
| 
14
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Math::Sumbolic::AuxFunctions is for the calcs of binomial coefficients.  | 
| 
15
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Parse::RecDescent parses the dice expressions.  | 
| 
17
 | 
29
 | 
 
 | 
 
 | 
  
29
  
 | 
 
 | 
32161
 | 
 use Math::Symbolic::AuxFunctions;  | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2863530
 | 
    | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1133
 | 
    | 
| 
18
 | 
29
 | 
 
 | 
 
 | 
  
29
  
 | 
 
 | 
58556
 | 
 use Parse::RecDescent;  | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
30093252
 | 
    | 
| 
 
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
296
 | 
    | 
| 
19
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
20
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Optional Modules  | 
| 
21
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Debug::ShowStuff is used by $self->debug() to descend and display hashes  | 
| 
22
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     in the object.  | 
| 
23
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
24
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Memoize is used by $self->new() to speed up calculations on individual  | 
| 
25
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     nodes in the expression tree.  If there are duplicate nodes, the second  | 
| 
26
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     and all subsequent calc_distribution calls with the same parameters will  | 
| 
27
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     just return the cached values.  | 
| 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #  | 
| 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Thanks to Mark Mills for the optional-module code snippet, and general  | 
| 
30
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     ideas, tips, and tricks for the whole module.  He's my local Perl  | 
| 
31
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Monk...you can email him thanks and offers of cookies at:  | 
| 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     extremely{plus}pm{at}hostile{dot}org  | 
| 
33
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 BEGIN {  | 
| 
34
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Is Debug::ShowStuff available?  | 
| 
35
 | 
29
 | 
  
 50
  
 | 
 
 | 
  
29
  
 | 
 
 | 
5194
 | 
     if ( eval q/ require Debug::ShowStuff / ) {  | 
| 
36
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# If so, import the routines.  | 
| 
37
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	Debug::ShowStuff->import("showref");  | 
| 
38
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
39
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Not available.  Place stub in its place.  | 
| 
40
 | 
29
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
1557
 | 
 	eval q/ sub showref { print @_; print "\n"; } /;  | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
 
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
    | 
| 
41
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
42
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
43
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Is Memoize available?  | 
| 
44
 | 
29
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
1722
 | 
     if ( eval q/ require Memoize / ) {  | 
| 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# If so, import the routines.  | 
| 
46
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
93772
 | 
 	Memoize->import();  | 
| 
47
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
48
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Not available.  Place stub in its place.  | 
| 
49
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	eval q/ sub memoize { return; } /;  | 
| 
50
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
51
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
53
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Binomial Coefficient Shortcut  | 
| 
54
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Non-polluted namespaces are cool and all, but sometimes it's too much to  | 
| 
55
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     type a 44 character long subroutine name multiple times in a simple  | 
| 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     equation.  Sheesh.  | 
| 
57
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $binco = \&Math::Symbolic::AuxFunctions::binomial_coeff;  | 
| 
58
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
59
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Recursive Parsing Grammar  | 
| 
60
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Parsing grammar and tree code outright stolen from Sam Holden's  | 
| 
61
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     DiceDistribution.pm at http://sam.holden.id.au/junk/DICEDISTRIBUTION/.  | 
| 
62
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Added a divide expression for division calculations.  Changed the way  | 
| 
63
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Fudge Dice are expressed to be #d[fF] which is more inline with how  | 
| 
64
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     they are represented elsewhere.  Added mid# dice expression as part  | 
| 
65
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     of dicenode.  | 
| 
66
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $DiceGrammar = <<'END_GRAMMAR';  | 
| 
67
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 expression:	add_sub end  { $item[1] }  | 
| 
68
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 add_sub:	mult_div '+' add_sub { { left => $item[1], op => '+', right => $item[3] } }  | 
| 
69
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 add_sub:	mult_div '-' add_sub { { left => $item[1], op => '-', right => $item[3] } }  | 
| 
70
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 add_sub:	mult_div  | 
| 
71
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 mult_div:	bracket '/' mult_div { { left => $item[1], op => '/', right => $item[3] } }  | 
| 
72
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 mult_div:	bracket '*' mult_div { { left => $item[1], op => '*', right => $item[3] } }  | 
| 
73
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 mult_div:	bracket  | 
| 
74
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 bracket:	'(' add_sub ')' { $item[2] }  | 
| 
75
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 bracket:	dicenode  | 
| 
76
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 dicenode:	/(\d+|mi)d(\d+|f)/i  | 
| 
77
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 dicenode:	/\d+/  | 
| 
78
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 end:		/\s*$/  | 
| 
79
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 END_GRAMMAR  | 
| 
80
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
81
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # Dice Parsing Object  | 
| 
82
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 my $DiceParser = Parse::RecDescent->new($DiceGrammar) || die("bad grammar");  | 
| 
83
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
84
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # import()  | 
| 
85
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Faux import function that either memoizes the calc portions of the  | 
| 
86
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     module (default) or doesn't.  | 
| 
87
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub import {  | 
| 
88
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Default is to memoize.  Saving cycles is a Good Thing.  However, if  | 
| 
89
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # someone passes an unmemoize argument, then we will respect their wishes.  | 
| 
90
 | 
29
 | 
  
 50
  
 | 
 
 | 
  
29
  
 | 
 
 | 
983
 | 
     if ( ! grep(/(un|no)memo(ize)*/i,@_) ) {  | 
| 
91
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Attempt to memoize the calculation subroutines.  This will either  | 
| 
92
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# truly memoize, or the stub memoize function will simply return having  | 
| 
93
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# done nothing.  | 
| 
94
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
555
 | 
 	memoize('calc_distribution');  | 
| 
95
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Not certain if calc_combination will benefit from Memoize.  Need to  | 
| 
96
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# test further.  | 
| 
97
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
8690
 | 
 	memoize('calc_combination');  | 
| 
98
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
99
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
100
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # All is well.  Return.  | 
| 
101
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
265266
 | 
     return;  | 
| 
102
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
103
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
104
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # debug()  | 
| 
105
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #    Print debugging information about object.  | 
| 
106
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub debug {  | 
| 
107
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # The object of our attention.  | 
| 
108
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
     my $self = shift;  | 
| 
109
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
110
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # For every piece of the object...  | 
| 
111
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     foreach my $key ( sort(keys(%$self)) ) {  | 
| 
112
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Output the name...  | 
| 
113
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	print "self->{$key}=";  | 
| 
114
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# And if it is a reference...  | 
| 
115
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	if ( ref($self->{$key}) ) {  | 
| 
116
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # Print the contents of the reference...  | 
| 
117
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	    print "\n";  | 
| 
118
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	    showref($self->{$key});  | 
| 
119
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} else {  | 
| 
120
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # Or, print the value.  | 
| 
121
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	    print $self->{$key} . "\n";  | 
| 
122
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
123
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
124
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
125
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # All is well.  | 
| 
126
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     return(0);  | 
| 
127
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
128
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
129
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # new(expression)  | 
| 
130
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Creates a new object based on the provided dice expression.  | 
| 
131
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub new {  | 
| 
132
 | 
29
 | 
 
 | 
 
 | 
  
29
  
 | 
  
0
  
 | 
461
 | 
     my $invocant = shift;  | 
| 
133
 | 
29
 | 
 
 | 
  
 33
  
 | 
 
 | 
 
 | 
318
 | 
     my $class = ref($invocant) || $invocant;  | 
| 
134
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
81
 | 
     my $self = {};  | 
| 
135
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
136
 | 
29
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
140
 | 
     die("must provide dice expression") unless (@_);  | 
| 
137
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
138
 | 
29
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
158
 | 
     if (@_ != 1) {  | 
| 
139
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	die("new() called with too many arguments");  | 
| 
140
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
141
 | 
29
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
509
 | 
 	$self->{EXPRESSION} = $DiceParser->expression(@_) || die "could not parse expression";  | 
| 
142
 | 
29
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
388373
 | 
 	$self->{DISTRIBUTION} = travel($self->{EXPRESSION}) || die "could not travel parsed expression";  | 
| 
143
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
144
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
145
 | 
29
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1424
 | 
     return(bless($self, $class));  | 
| 
146
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
147
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
148
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # travel(expression)  | 
| 
149
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Travel the parsed expression, returning hash of value => permutations.  | 
| 
150
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub travel {  | 
| 
151
 | 
61
 | 
 
 | 
  
 50
  
 | 
  
61
  
 | 
  
0
  
 | 
382
 | 
     my $node = shift || die "invalid or missing expression node";  | 
| 
152
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
153
 | 
61
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
212
 | 
     if ( ref($node) ) {  | 
| 
154
 | 
16
 | 
 
 | 
 
 | 
 
 | 
 
 | 
48
 | 
 	for ($node->{op}) {  | 
| 
155
 | 
16
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
110
 | 
 	    /(\+)/ && return( calc_combination($1, travel($node->{left}), travel($node->{right})) );  | 
| 
156
 | 
9
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
62
 | 
 	    /(\-)/ && return( calc_combination($1, travel($node->{left}), travel($node->{right})) );  | 
| 
157
 | 
6
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
47
 | 
 	    /(\*)/ && return( calc_combination($1, travel($node->{left}), travel($node->{right})) );  | 
| 
158
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
43
 | 
 	    /(\/)/ && return( calc_combination($1, travel($node->{left}), travel($node->{right})) );  | 
| 
159
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
160
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
161
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
162
 | 
45
 | 
 
 | 
 
 | 
 
 | 
 
 | 
133
 | 
     for ($node) {  | 
| 
163
 | 
45
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
1256
 | 
 	/^(\d+)(d)(\d+|f)/i && return( calc_distribution($2,$1,$3) );  | 
| 
164
 | 
8
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
208
 | 
 	/^(mi)(d)(\d+|f)/i && return( calc_distribution("m",3,$3) );  | 
| 
165
 | 
2
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
49
 | 
 	/^\d+$/ && return( {$node => 1} );  | 
| 
166
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
167
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
168
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     die("invalid token in expression.");  | 
| 
169
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
170
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
171
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # calc_distribution(method,numdice,numsides)  | 
| 
172
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Calculate distribution of values/permutations given: method, number of  | 
| 
173
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     dice, and number of sides per die.  | 
| 
174
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub calc_distribution {  | 
| 
175
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # The dice method requested.  | 
| 
176
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $method = shift;  | 
| 
177
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # The number of dice.  | 
| 
178
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $n = shift; $n += 0;  | 
| 
179
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # The number of sides/faces on each die.  | 
| 
180
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     #     f/F = Fudge dice = d3.  | 
| 
181
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $s = shift; $s += 0 unless $s =~ /f/i;  | 
| 
182
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Loop value based on total/face.  | 
| 
183
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $t1;  | 
| 
184
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Hash to return containing the distribution.  | 
| 
185
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my %dist;  | 
| 
186
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
187
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # If dice node method is simple-sum nDs or nDf...  | 
| 
188
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     if ( $method =~ /d/i ) {  | 
| 
189
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	if ( $s =~ /f/i ) {  | 
| 
190
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # If dice node method is fudge nDf...  | 
| 
191
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
192
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # First, get the distribution for nDs...  | 
| 
193
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    my $tempdist = calc_distribution("d",$n,3);  | 
| 
194
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # Then loop to build the new distribution from (-n..0)...  | 
| 
195
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    my $origkey = $n;  | 
| 
196
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    for ($t1=-1 * $n; $t1 <= 0; $t1++) {  | 
| 
197
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Changing the original values in (n..ns) to (-n..n),  | 
| 
198
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# copy the original distribution to the new one with  | 
| 
199
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# the correct values...  | 
| 
200
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$dist{$t1} = $$tempdist{$origkey};  | 
| 
201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$dist{$t1*-1} = $$tempdist{$origkey};  | 
| 
202
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$origkey++;  | 
| 
203
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    }  | 
| 
204
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} elsif ( $n eq 1 ) {  | 
| 
205
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # Save compute cycles if only one die...  | 
| 
206
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
207
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # Each face has a single chance...  | 
| 
208
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    for ($t1=1; $t1 <= $s; $t1++) {  | 
| 
209
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$dist{$t1} = 1;  | 
| 
210
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    }  | 
| 
211
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} else {  | 
| 
212
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # Else compute number of combinations for a total on the face of n  | 
| 
213
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # dice.  | 
| 
214
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
215
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # Minimum and maximum sums.  | 
| 
216
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    my $tmin = $n;  | 
| 
217
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    my $tmax = $n*$s;  | 
| 
218
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # Peak sum is the sum around which the bell-curve mirrors, saving  | 
| 
219
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # compute time.  | 
| 
220
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    my $tpeak = ($tmin+$tmax)/2;  | 
| 
221
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
222
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # For each total (t1) in $tmin..$tmax, calculate the number of  | 
| 
223
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # combinations giving that total.  | 
| 
224
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    for ($t1=$tmin; $t1 <= $tpeak; $t1++) {  | 
| 
225
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Total (t2) that is the mirror point in the bell curve.  | 
| 
226
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		my $t2 = $tmin + $tmax - $t1;  | 
| 
227
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Ceiling for the sum function.  | 
| 
228
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		my $ceil = int( ($t1-$n) / $s );  | 
| 
229
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Result.  | 
| 
230
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		my $res = 0;  | 
| 
231
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Loop control for the sum funtion.  | 
| 
232
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		my $k;  | 
| 
233
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
234
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Sum Function: For each k in 0 to $ceil...  | 
| 
235
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		for ($k=0; $k <= $ceil; $k++) {  | 
| 
236
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		    # Calculate and add to previous results.  | 
| 
237
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		    $res += ((-1)**$k) * &$binco($n,$k) * &$binco(($t1-($s*$k)-1),($n-1));  | 
| 
238
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
239
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
240
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		# Set the mirrored points of the distribution.  | 
| 
241
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		#     Note: $t1 and $t2 can be equal once at   | 
| 
242
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		#     $t1=$tpeak when s is even.  | 
| 
243
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$dist{$t1} = $res;  | 
| 
244
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		$dist{$t2} = $res;  | 
| 
245
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    }  | 
| 
246
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
247
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } elsif ( $method =~ /m/i ) {  | 
| 
248
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# If dice method is take-the-middle-value nMs...  | 
| 
249
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
250
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Minimum and maximum values.  | 
| 
251
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	my $tmin = 1;  | 
| 
252
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	my $tmax = $s;  | 
| 
253
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
254
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# For each value (t1) in $tmin..$tmax, calculate the number of  | 
| 
255
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# combinations giving t1 as the middle face value.  | 
| 
256
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	for ($t1=$tmin; $t1 <= $tmax; $t1++) {  | 
| 
257
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    $dist{$t1} = 1 + ( 3 * ($s - 1) ) + ( 6 * ($t1 - 1) * ($s - $t1) );  | 
| 
258
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
259
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
260
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
261
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Return the value=>combinations distribution hash for this node.  | 
| 
262
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return({%dist});  | 
| 
263
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
264
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
265
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # calc_combination(operand,distribution1,distribution2)  | 
| 
266
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Combine two distributions with the method provided.  | 
| 
267
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub calc_combination {  | 
| 
268
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Calculation to perform on values.  | 
| 
269
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $op = shift;  | 
| 
270
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Distributions to combine.  | 
| 
271
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $dist1 = shift;  | 
| 
272
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my $dist2 = shift;  | 
| 
273
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # The combined distribution.  | 
| 
274
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     my %cdist;  | 
| 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
276
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # For each value in the first distribution...  | 
| 
277
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     foreach my $val1 ( sort {$a+0 <=> $b+0} keys(%$dist1) ) {  | 
| 
278
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	# Combine it with every value in the second distribution...  | 
| 
279
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	foreach my $val2 ( sort {$a+0 <=> $b+0} keys(%$dist2) ) {  | 
| 
280
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # The new value of which is calculated based on combine method...  | 
| 
281
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    my $newval;  | 
| 
282
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    for ($op) {  | 
| 
283
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		/\+/ && do { $newval = $val1 + $val2 };  | 
| 
284
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		/\-/ && do { $newval = $val1 - $val2 };  | 
| 
285
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		/\*/ && do { $newval = $val1 * $val2 };  | 
| 
286
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		/\// && do { $newval = int($val1 / $val2) };  | 
| 
287
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    }  | 
| 
288
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # Calculate the new combined combinations and set it to the new  | 
| 
289
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    # value in the new distribution.  | 
| 
290
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    $cdist{$newval} += $$dist1{$val1} * $$dist2{$val2};  | 
| 
291
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
292
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
293
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
294
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     # Return the combined distribution.  | 
| 
295
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     return({%cdist});  | 
| 
296
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
297
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
298
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # combinations(targetvalue)  | 
| 
299
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Calculate number of combinations for a target value.  | 
| 
300
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub combinations {  | 
| 
301
 | 
303
 | 
 
 | 
 
 | 
  
303
  
 | 
  
0
  
 | 
547
 | 
     my $self = shift;  | 
| 
302
 | 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
378
 | 
     my $targetvalue = shift;  | 
| 
303
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
304
 | 
303
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
738
 | 
     $targetvalue = "ALL" unless defined($targetvalue);  | 
| 
305
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
306
 | 
303
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
651
 | 
     if ( ref($self) ) {  | 
| 
307
 | 
303
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
608
 | 
 	if (@_ != 0) {  | 
| 
308
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	    die("combinations() called incorrectly");  | 
| 
309
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} else {  | 
| 
310
 | 
303
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
679
 | 
 	    if ( $targetvalue eq "ALL" ) {  | 
| 
311
 | 
28
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
133
 | 
 		if ( $self->{COMBINATIONS} ) {  | 
| 
312
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		    return ( $self->{COMBINATIONS} );  | 
| 
313
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		} else {  | 
| 
314
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
56
 | 
 		    foreach my $value ( values(%{$self->{DISTRIBUTION}}) ) {  | 
| 
 
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
880
 | 
    | 
| 
315
 | 
12201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
12281
 | 
 			$self->{COMBINATIONS} += $value;  | 
| 
316
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		    }  | 
| 
317
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
125
 | 
 		    return($self->{COMBINATIONS});  | 
| 
318
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
319
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    } else {  | 
| 
320
 | 
275
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1355
 | 
 		return( $self->{DISTRIBUTION}->{$targetvalue} );  | 
| 
321
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    }  | 
| 
322
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
323
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
324
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	die("combinations() called on non-object");  | 
| 
325
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
326
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
327
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
328
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # distribution()  | 
| 
329
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Returns a hash containing the distribution in value=>combinations format.  | 
| 
330
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub distribution {  | 
| 
331
 | 
0
 | 
 
 | 
 
 | 
  
0
  
 | 
  
0
  
 | 
0
 | 
     my $self = shift;  | 
| 
332
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
333
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
     if ( ref($self) ) {  | 
| 
334
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	if (@_ != 0) {  | 
| 
335
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	    die("distribution() called with argument on object");  | 
| 
336
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} else {  | 
| 
337
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	    return( $self->{DISTRIBUTION} );  | 
| 
338
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
339
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
340
 | 
0
 | 
  
  0
  
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	if (@_ != 0) {  | 
| 
341
 | 
0
 | 
 
 | 
  
  0
  
 | 
 
 | 
 
 | 
0
 | 
 	    my $expression = $DiceParser->expression(@_) || die "could not parse expression";  | 
| 
342
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	    return( travel($expression) );  | 
| 
343
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} else {  | 
| 
344
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	    die("no expression provided for non-object distribution() call");  | 
| 
345
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
346
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
347
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
348
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
349
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # probability(targetvalue)  | 
| 
350
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Returns the probability for targetvalue, or a hash of probabilities in   | 
| 
351
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     value=>probability format.  | 
| 
352
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub probability {  | 
| 
353
 | 
12201
 | 
 
 | 
 
 | 
  
12201
  
 | 
  
0
  
 | 
17924
 | 
     my $self = shift;  | 
| 
354
 | 
12201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
13823
 | 
     my $targetvalue = shift;  | 
| 
355
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
356
 | 
12201
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
23596
 | 
     $targetvalue = "ALL" unless defined($targetvalue);  | 
| 
357
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
358
 | 
12201
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
22107
 | 
     if ( ref($self) ) {  | 
| 
359
 | 
12201
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
21747
 | 
 	if (@_ != 0) {  | 
| 
360
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	    die("probability() called incorrectly");  | 
| 
361
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} else {  | 
| 
362
 | 
12201
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
27573
 | 
 	    if ( ! exists($self->{PROBABILITIES}) ) {  | 
| 
363
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
157
 | 
 		my $combs = $self->combinations();  | 
| 
364
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
67
 | 
 		my %probs;  | 
| 
365
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
71
 | 
 		foreach my $value ( keys(%{$self->{DISTRIBUTION}}) ) {  | 
| 
 
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
36321
 | 
    | 
| 
366
 | 
12201
 | 
 
 | 
 
 | 
 
 | 
 
 | 
26113
 | 
 		    $probs{$value} = $self->{DISTRIBUTION}->{$value} / $combs;  | 
| 
367
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 		}  | 
| 
368
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
11049
 | 
 		$self->{PROBABILITIES} = {%probs};  | 
| 
369
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    }  | 
| 
370
 | 
12201
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
22987
 | 
 	    if ( $targetvalue eq "ALL" ) {  | 
| 
371
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 		return( $self->{PROBABILITIES} );  | 
| 
372
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    } else {  | 
| 
373
 | 
12201
 | 
 
 | 
  
 50
  
 | 
 
 | 
 
 | 
78851
 | 
 		return( $self->{PROBABILITIES}->{$targetvalue} || undef );  | 
| 
374
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    }  | 
| 
375
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
376
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
377
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	die("probability() called on non-object");  | 
| 
378
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
379
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
380
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
381
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # bounds()  | 
| 
382
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Returns the min and max values of the valueset.  | 
| 
383
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub bounds {  | 
| 
384
 | 
56
 | 
 
 | 
 
 | 
  
56
  
 | 
  
0
  
 | 
125
 | 
     my $self = shift;  | 
| 
385
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
386
 | 
56
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
190
 | 
     if ( ref($self) ) {  | 
| 
387
 | 
56
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
180
 | 
 	if (@_ != 0) {  | 
| 
388
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	    die("bounds() called with argument on object");  | 
| 
389
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} else {  | 
| 
390
 | 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
161
 | 
 	    return( [ $self->min(), $self->max() ] );  | 
| 
391
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
392
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
393
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	die("bounds() called on non-object");  | 
| 
394
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
395
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
396
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
397
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # max()  | 
| 
398
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Returns the max value of the valueset.  | 
| 
399
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub max {  | 
| 
400
 | 
84
 | 
 
 | 
 
 | 
  
84
  
 | 
  
0
  
 | 
160
 | 
     my $self = shift;  | 
| 
401
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
402
 | 
84
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
220
 | 
     if ( ref($self) ) {  | 
| 
403
 | 
84
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
236
 | 
 	if (@_ != 0) {  | 
| 
404
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	    die("max() called with argument on object");  | 
| 
405
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} else {  | 
| 
406
 | 
84
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
210
 | 
 	    if ( $self->{MAX} ) {  | 
| 
407
 | 
56
 | 
 
 | 
 
 | 
 
 | 
 
 | 
401
 | 
 		return( $self->{MAX} );  | 
| 
408
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    } else {  | 
| 
409
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
58
 | 
 		my @values = sort {$b+0 <=> $a+0} keys(%{$self->{DISTRIBUTION}});  | 
| 
 
 | 
145546
 | 
 
 | 
 
 | 
 
 | 
 
 | 
170861
 | 
    | 
| 
 
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
2033
 | 
    | 
| 
410
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1013
 | 
 		$self->{MAX} = shift(@values);  | 
| 
411
 | 
28
 | 
 
 | 
 
 | 
 
 | 
 
 | 
726
 | 
 		return( $self->{MAX} );  | 
| 
412
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    }  | 
| 
413
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
414
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
415
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	die("max() called on non-object");  | 
| 
416
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
417
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
418
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
419
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 # min()  | 
| 
420
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 #     Returns the min value of the valueset.  | 
| 
421
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 sub min {  | 
| 
422
 | 
84
 | 
 
 | 
 
 | 
  
84
  
 | 
  
0
  
 | 
12866
 | 
     my $self = shift;  | 
| 
423
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
424
 | 
84
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
292
 | 
     if ( ref($self) ) {  | 
| 
425
 | 
84
 | 
  
 50
  
 | 
 
 | 
 
 | 
 
 | 
234
 | 
 	if (@_ != 0) {  | 
| 
426
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
0
 | 
 	    die("min() called with argument on object");  | 
| 
427
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	} else {  | 
| 
428
 | 
84
 | 
  
100
  
 | 
 
 | 
 
 | 
 
 | 
508
 | 
 	    if ( $self->{MIN} ) {  | 
| 
429
 | 
52
 | 
 
 | 
 
 | 
 
 | 
 
 | 
225
 | 
 		return( $self->{MIN} );  | 
| 
430
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    } else {  | 
| 
431
 | 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
98
 | 
 		my @values = sort {$a+0 <=> $b+0} keys(%{$self->{DISTRIBUTION}});  | 
| 
 
 | 
145751
 | 
 
 | 
 
 | 
 
 | 
 
 | 
168339
 | 
    | 
| 
 
 | 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1538
 | 
    | 
| 
432
 | 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1546
 | 
 		$self->{MIN} = shift(@values);  | 
| 
433
 | 
32
 | 
 
 | 
 
 | 
 
 | 
 
 | 
1235
 | 
 		return( $self->{MIN} );  | 
| 
434
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	    }  | 
| 
435
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	}  | 
| 
436
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     } else {  | 
| 
437
 | 
0
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 	die("min() called on non-object");  | 
| 
438
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
     }  | 
| 
439
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 }  | 
| 
440
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
441
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 1;  | 
| 
442
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
    | 
| 
443
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 
 | 
 __END__  |