Commit | Line | Data |
---|---|---|
a0d0e21e LW |
1 | #!./perl |
2 | ||
ec761cee TP |
3 | # From Tom Phoenix <rootbeer@teleport.com> 22 Feb 1997 |
4 | # Based upon a test script by kgb@ast.cam.ac.uk (Karl Glazebrook) | |
a0d0e21e | 5 | |
ec761cee TP |
6 | # Looking for the hints? You're in the right place. |
7 | # The hints are near each test, so search for "TEST #", where | |
8 | # the pound sign is replaced by the number of the test. | |
a0d0e21e | 9 | |
ec761cee TP |
10 | # I'd like to include some more robust tests, but anything |
11 | # too subtle to be detected here would require a time-consuming | |
12 | # test. Also, of course, we're here to detect only flaws in Perl; | |
13 | # if there are flaws in the underlying system rand, that's not | |
14 | # our responsibility. But if you want better tests, see | |
15 | # The Art of Computer Programming, Donald E. Knuth, volume 2, | |
16 | # chapter 3. ISBN 0-201-03822-6 (v. 2) | |
a0d0e21e | 17 | |
ec761cee TP |
18 | BEGIN { |
19 | chdir "t" if -d "t"; | |
69026470 | 20 | @INC = qw(. ../lib); |
748a9306 | 21 | } |
748a9306 | 22 | |
ec761cee TP |
23 | use strict; |
24 | use Config; | |
69026470 | 25 | |
1ae3d757 | 26 | require "./test.pl"; |
3524d3b9 | 27 | |
ec761cee | 28 | |
7d9942e3 | 29 | my $reps = 100_000; # How many times to try rand each time. |
ec761cee TP |
30 | # May be changed, but should be over 500. |
31 | # The more the better! (But slower.) | |
32 | ||
7d9942e3 DM |
33 | my $bits = 8; # how many significant bits we check on each random number |
34 | my $nslots = (1<< $bits); # how many different numbers | |
35 | ||
36 | plan(tests => 7 + $nslots); | |
a0d0e21e | 37 | |
7d9942e3 DM |
38 | # First, let's see whether randbits is set right and that rand() returns |
39 | # an even distribution of values | |
ec761cee | 40 | { |
7d9942e3 DM |
41 | my $sum; |
42 | my @slots = (0) x $nslots; | |
43 | my $prob = 1/$nslots; # probability of a particular slot being | |
44 | # on a particular iteration | |
45 | ||
46 | # We are going to generate $reps random numbers, each in the range | |
47 | # 0..$nslots-1. They should be evenly distributed. We use @slots to | |
48 | # count the number of occurrences of each number. For each count, we | |
49 | # check that it is in the range we expect. For example for reps = | |
50 | # 100_000 and using 8 bits, we expect each count to be around | |
51 | # 100_000/256 = 390. How much around it we tolerate depends on the | |
52 | # standard deviation, and how many deviations we allow. If we allow | |
53 | # 6-sigmas, then that means that in only 1 run in 506e6 will be get a | |
54 | # failure by chance, assuming a fair random number generator. Given | |
55 | # that we test each slot, the overall chance of a false negative in | |
56 | # this test script is about 1 in 2e6, assuming 256 slots. | |
57 | # | |
58 | # the actual count in a slot should follow a binomial distribution | |
59 | # (e.g. rolling 18 dice, we 'expect' to see 3 sixes, but there's | |
60 | # actually a 24% chance of 3, a 20% change of 2 or 4, a 12% | |
61 | # chance of 1 or 5, and a 4% chance of 0 or 6 of them). | |
62 | # | |
63 | # This makes it easy to calculate the expected mean a standard | |
64 | # deviation; see | |
65 | # https://en.wikipedia.org/wiki/Binomial_distribution#Variance | |
66 | ||
67 | my $mean = $reps * $prob; | |
68 | my $stddev = sqrt($reps * $prob * (1 - $prob)); | |
69 | my $sigma6 = $stddev * 6.0; # very unlikely to be outside that range | |
70 | my $min = $mean - $sigma6; | |
71 | my $max = $mean + $sigma6; | |
72 | ||
73 | note("reps=$reps; slots=$nslots; min=$min mean=$mean max=$max"); | |
74 | ||
ec761cee TP |
75 | for (1..$reps) { |
76 | my $n = rand(1); | |
ce9935e0 | 77 | if ($n < 0.0 or $n >= 1.0) { |
f0bf11b2 DM |
78 | diag(<<EOM); |
79 | WHOA THERE! \$Config{drand01} is set to '$Config{drand01}', | |
80 | but that apparently produces values ($n) < 0.0 or >= 1.0. | |
81 | Make sure \$Config{drand01} is a valid expression in the | |
82 | C-language, and produces values in the range [0.0,1.0). | |
83 | ||
84 | I give up. | |
ce9935e0 GS |
85 | EOM |
86 | exit; | |
87 | } | |
7d9942e3 | 88 | $slots[int($n * $nslots)]++; |
ec761cee TP |
89 | } |
90 | ||
7d9942e3 DM |
91 | for my $i (0..$nslots - 1) { |
92 | # this test should randomly fail very rarely. If it fails | |
93 | # for you, try re-running this test script a few more times; | |
94 | # if it goes away, it was likely a random (ha ha!) glitch. | |
95 | # If you keep seeing failures, it means your random number | |
96 | # generator is producing a very uneven spread of values. | |
97 | ok($slots[$i] >= $min && $slots[$i] <= $max, "checking slot $i") | |
98 | or diag("slot $i; count $slots[$i] outside expected range $min..$max"); | |
ec761cee | 99 | } |
748a9306 | 100 | } |
748a9306 | 101 | |
a0d0e21e | 102 | |
ec761cee TP |
103 | # Now, let's see whether rand accepts its argument |
104 | { | |
105 | my($max, $min); | |
106 | $max = $min = rand(100); | |
107 | for (1..$reps) { | |
108 | my $n = rand(100); | |
109 | $max = $n if $n > $max; | |
110 | $min = $n if $n < $min; | |
111 | } | |
112 | ||
ec761cee TP |
113 | # This test checks to see that rand(100) really falls |
114 | # within the range 0 - 100, and that the numbers produced | |
115 | # have a reasonably-large range among them. | |
116 | # | |
8b002d47 DM |
117 | cmp_ok($min, '>=', 0, "rand(100) >= 0"); |
118 | cmp_ok($max, '<', 100, "rand(100) < 100"); | |
119 | cmp_ok($max - $min, '>=', 65, "rand(100) in 65 range"); | |
3524d3b9 | 120 | |
9be67dbc | 121 | |
ec761cee TP |
122 | # This test checks that rand without an argument |
123 | # is equivalent to rand(1). | |
124 | # | |
125 | $_ = 12345; # Just for fun. | |
126 | srand 12345; | |
127 | my $r = rand; | |
128 | srand 12345; | |
9be67dbc MS |
129 | is(rand(1), $r, 'rand() without args is rand(1)'); |
130 | ||
748a9306 | 131 | |
ec761cee TP |
132 | # This checks that rand without an argument is not |
133 | # rand($_). (In case somebody got overzealous.) | |
134 | # | |
8b002d47 | 135 | cmp_ok($r, '<', 1, 'rand() without args is under 1'); |
ec761cee TP |
136 | } |
137 | ||
3be8f094 TC |
138 | { # [perl #115928] use a standard rand() implementation |
139 | srand(1); | |
140 | is(int rand(1000), 41, "our own implementation behaves consistently"); | |
141 | is(int rand(1000), 454, "and still consistently"); | |
142 | } |