This is a live mirror of the Perl 5 development currently hosted at https://github.com/perl/perl5
[perl #125540] handle already being at EOF while not finding a heredoc terminator
[perl5.git] / t / op / rand.t
CommitLineData
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
18BEGIN {
19 chdir "t" if -d "t";
69026470 20 @INC = qw(. ../lib);
748a9306 21}
748a9306 22
ec761cee
TP
23use strict;
24use Config;
69026470 25
1ae3d757 26require "./test.pl";
3524d3b9 27
ec761cee 28
7d9942e3 29my $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
33my $bits = 8; # how many significant bits we check on each random number
34my $nslots = (1<< $bits); # how many different numbers
35
36plan(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);
79WHOA THERE! \$Config{drand01} is set to '$Config{drand01}',
80but that apparently produces values ($n) < 0.0 or >= 1.0.
81Make sure \$Config{drand01} is a valid expression in the
82C-language, and produces values in the range [0.0,1.0).
83
84I give up.
ce9935e0
GS
85EOM
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}