xref: /onnv-gate/usr/src/cmd/perl/5.8.4/distrib/t/op/rand.t (revision 0:68f95e015346)
1*0Sstevel@tonic-gate#!./perl
2*0Sstevel@tonic-gate
3*0Sstevel@tonic-gate# From Tom Phoenix <rootbeer@teleport.com> 22 Feb 1997
4*0Sstevel@tonic-gate# Based upon a test script by kgb@ast.cam.ac.uk (Karl Glazebrook)
5*0Sstevel@tonic-gate
6*0Sstevel@tonic-gate# Looking for the hints? You're in the right place.
7*0Sstevel@tonic-gate# The hints are near each test, so search for "TEST #", where
8*0Sstevel@tonic-gate# the pound sign is replaced by the number of the test.
9*0Sstevel@tonic-gate
10*0Sstevel@tonic-gate# I'd like to include some more robust tests, but anything
11*0Sstevel@tonic-gate# too subtle to be detected here would require a time-consuming
12*0Sstevel@tonic-gate# test. Also, of course, we're here to detect only flaws in Perl;
13*0Sstevel@tonic-gate# if there are flaws in the underlying system rand, that's not
14*0Sstevel@tonic-gate# our responsibility. But if you want better tests, see
15*0Sstevel@tonic-gate# The Art of Computer Programming, Donald E. Knuth, volume 2,
16*0Sstevel@tonic-gate# chapter 3. ISBN 0-201-03822-6 (v. 2)
17*0Sstevel@tonic-gate
18*0Sstevel@tonic-gateBEGIN {
19*0Sstevel@tonic-gate    chdir "t" if -d "t";
20*0Sstevel@tonic-gate    @INC = qw(. ../lib);
21*0Sstevel@tonic-gate}
22*0Sstevel@tonic-gate
23*0Sstevel@tonic-gateuse strict;
24*0Sstevel@tonic-gateuse Config;
25*0Sstevel@tonic-gate
26*0Sstevel@tonic-gaterequire "test.pl";
27*0Sstevel@tonic-gateplan(tests => 8);
28*0Sstevel@tonic-gate
29*0Sstevel@tonic-gate
30*0Sstevel@tonic-gatemy $reps = 10000;	# How many times to try rand each time.
31*0Sstevel@tonic-gate			# May be changed, but should be over 500.
32*0Sstevel@tonic-gate			# The more the better! (But slower.)
33*0Sstevel@tonic-gate
34*0Sstevel@tonic-gatesub bits ($) {
35*0Sstevel@tonic-gate    # Takes a small integer and returns the number of one-bits in it.
36*0Sstevel@tonic-gate    my $total;
37*0Sstevel@tonic-gate    my $bits = sprintf "%o", $_[0];
38*0Sstevel@tonic-gate    while (length $bits) {
39*0Sstevel@tonic-gate	$total += (0,1,1,2,1,2,2,3)[chop $bits];	# Oct to bits
40*0Sstevel@tonic-gate    }
41*0Sstevel@tonic-gate    $total;
42*0Sstevel@tonic-gate}
43*0Sstevel@tonic-gate
44*0Sstevel@tonic-gate# First, let's see whether randbits is set right
45*0Sstevel@tonic-gate{
46*0Sstevel@tonic-gate    my($max, $min, $sum);	# Characteristics of rand
47*0Sstevel@tonic-gate    my($off, $shouldbe);	# Problems with randbits
48*0Sstevel@tonic-gate    my($dev, $bits);		# Number of one bits
49*0Sstevel@tonic-gate    my $randbits = $Config{randbits};
50*0Sstevel@tonic-gate    $max = $min = rand(1);
51*0Sstevel@tonic-gate    for (1..$reps) {
52*0Sstevel@tonic-gate	my $n = rand(1);
53*0Sstevel@tonic-gate	if ($n < 0.0 or $n >= 1.0) {
54*0Sstevel@tonic-gate	    print <<EOM;
55*0Sstevel@tonic-gate# WHOA THERE!  \$Config{drand01} is set to '$Config{drand01}',
56*0Sstevel@tonic-gate# but that apparently produces values < 0.0 or >= 1.0.
57*0Sstevel@tonic-gate# Make sure \$Config{drand01} is a valid expression in the
58*0Sstevel@tonic-gate# C-language, and produces values in the range [0.0,1.0).
59*0Sstevel@tonic-gate#
60*0Sstevel@tonic-gate# I give up.
61*0Sstevel@tonic-gateEOM
62*0Sstevel@tonic-gate	    exit;
63*0Sstevel@tonic-gate	}
64*0Sstevel@tonic-gate	$sum += $n;
65*0Sstevel@tonic-gate	$bits += bits($n * 256);	# Don't be greedy; 8 is enough
66*0Sstevel@tonic-gate		    # It's too many if randbits is less than 8!
67*0Sstevel@tonic-gate		    # But that should never be the case... I hope.
68*0Sstevel@tonic-gate		    # Note: If you change this, you must adapt the
69*0Sstevel@tonic-gate		    # formula for absolute standard deviation, below.
70*0Sstevel@tonic-gate	$max = $n if $n > $max;
71*0Sstevel@tonic-gate	$min = $n if $n < $min;
72*0Sstevel@tonic-gate    }
73*0Sstevel@tonic-gate
74*0Sstevel@tonic-gate
75*0Sstevel@tonic-gate    # This test checks for one of Perl's most frequent
76*0Sstevel@tonic-gate    # mis-configurations. Your system's documentation
77*0Sstevel@tonic-gate    # for rand(2) should tell you what value you need
78*0Sstevel@tonic-gate    # for randbits. Usually the diagnostic message
79*0Sstevel@tonic-gate    # has the right value as well. Just fix it and
80*0Sstevel@tonic-gate    # recompile, and you'll usually be fine. (The main
81*0Sstevel@tonic-gate    # reason that the diagnostic message might get the
82*0Sstevel@tonic-gate    # wrong value is that Config.pm is incorrect.)
83*0Sstevel@tonic-gate    #
84*0Sstevel@tonic-gate    unless (ok( !$max <= 0 or $max >= (2 ** $randbits))) {# Just in case...
85*0Sstevel@tonic-gate	print <<DIAG;
86*0Sstevel@tonic-gate# max=[$max] min=[$min]
87*0Sstevel@tonic-gate# This perl was compiled with randbits=$randbits
88*0Sstevel@tonic-gate# which is _way_ off. Or maybe your system rand is broken,
89*0Sstevel@tonic-gate# or your C compiler can't multiply, or maybe Martians
90*0Sstevel@tonic-gate# have taken over your computer. For starters, see about
91*0Sstevel@tonic-gate# trying a better value for randbits, probably smaller.
92*0Sstevel@tonic-gateDIAG
93*0Sstevel@tonic-gate
94*0Sstevel@tonic-gate	# If that isn't the problem, we'll have
95*0Sstevel@tonic-gate	# to put d_martians into Config.pm
96*0Sstevel@tonic-gate	print "# Skipping remaining tests until randbits is fixed.\n";
97*0Sstevel@tonic-gate	exit;
98*0Sstevel@tonic-gate    }
99*0Sstevel@tonic-gate
100*0Sstevel@tonic-gate    $off = log($max) / log(2);			# log2
101*0Sstevel@tonic-gate    $off = int($off) + ($off > 0);		# Next more positive int
102*0Sstevel@tonic-gate    unless (is( $off, 0 )) {
103*0Sstevel@tonic-gate	$shouldbe = $Config{randbits} + $off;
104*0Sstevel@tonic-gate	print "# max=[$max] min=[$min]\n";
105*0Sstevel@tonic-gate	print "# This perl was compiled with randbits=$randbits on $^O.\n";
106*0Sstevel@tonic-gate	print "# Consider using randbits=$shouldbe instead.\n";
107*0Sstevel@tonic-gate	# And skip the remaining tests; they would be pointless now.
108*0Sstevel@tonic-gate	print "# Skipping remaining tests until randbits is fixed.\n";
109*0Sstevel@tonic-gate	exit;
110*0Sstevel@tonic-gate    }
111*0Sstevel@tonic-gate
112*0Sstevel@tonic-gate
113*0Sstevel@tonic-gate    # This should always be true: 0 <= rand(1) < 1
114*0Sstevel@tonic-gate    # If this test is failing, something is seriously wrong,
115*0Sstevel@tonic-gate    # either in perl or your system's rand function.
116*0Sstevel@tonic-gate    #
117*0Sstevel@tonic-gate    unless (ok( !($min < 0 or $max >= 1) )) {	# Slightly redundant...
118*0Sstevel@tonic-gate	print "# min too low\n" if $min < 0;
119*0Sstevel@tonic-gate	print "# max too high\n" if $max >= 1;
120*0Sstevel@tonic-gate    }
121*0Sstevel@tonic-gate
122*0Sstevel@tonic-gate
123*0Sstevel@tonic-gate    # This is just a crude test. The average number produced
124*0Sstevel@tonic-gate    # by rand should be about one-half. But once in a while
125*0Sstevel@tonic-gate    # it will be relatively far away. Note: This test will
126*0Sstevel@tonic-gate    # occasionally fail on a perfectly good system!
127*0Sstevel@tonic-gate    # See the hints for test 4 to see why.
128*0Sstevel@tonic-gate    #
129*0Sstevel@tonic-gate    $sum /= $reps;
130*0Sstevel@tonic-gate    unless (ok( !($sum < 0.4 or $sum > 0.6) )) {
131*0Sstevel@tonic-gate	print "# Average random number is far from 0.5\n";
132*0Sstevel@tonic-gate    }
133*0Sstevel@tonic-gate
134*0Sstevel@tonic-gate
135*0Sstevel@tonic-gate    #   NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
136*0Sstevel@tonic-gate    # This test will fail .1% of the time on a normal system.
137*0Sstevel@tonic-gate    #				also
138*0Sstevel@tonic-gate    # This test asks you to see these hints 100% of the time!
139*0Sstevel@tonic-gate    #   NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE NOTE
140*0Sstevel@tonic-gate    #
141*0Sstevel@tonic-gate    # There is probably no reason to be alarmed that
142*0Sstevel@tonic-gate    # something is wrong with your rand function. But,
143*0Sstevel@tonic-gate    # if you're curious or if you can't help being
144*0Sstevel@tonic-gate    # alarmed, keep reading.
145*0Sstevel@tonic-gate    #
146*0Sstevel@tonic-gate    # This is a less-crude test than test 3. But it has
147*0Sstevel@tonic-gate    # the same basic flaw: Unusually distributed random
148*0Sstevel@tonic-gate    # values should occasionally appear in every good
149*0Sstevel@tonic-gate    # random number sequence. (If you flip a fair coin
150*0Sstevel@tonic-gate    # twenty times every day, you'll see it land all
151*0Sstevel@tonic-gate    # heads about one time in a million days, on the
152*0Sstevel@tonic-gate    # average. That might alarm you if you saw it happen
153*0Sstevel@tonic-gate    # on the first day!)
154*0Sstevel@tonic-gate    #
155*0Sstevel@tonic-gate    # So, if this test failed on you once, run it a dozen
156*0Sstevel@tonic-gate    # times. If it keeps failing, it's likely that your
157*0Sstevel@tonic-gate    # rand is bogus. If it keeps passing, it's likely
158*0Sstevel@tonic-gate    # that the one failure was bogus. If it's a mix,
159*0Sstevel@tonic-gate    # read on to see about how to interpret the tests.
160*0Sstevel@tonic-gate    #
161*0Sstevel@tonic-gate    # The number printed in square brackets is the
162*0Sstevel@tonic-gate    # standard deviation, a statistical measure
163*0Sstevel@tonic-gate    # of how unusual rand's behavior seemed. It should
164*0Sstevel@tonic-gate    # fall in these ranges with these *approximate*
165*0Sstevel@tonic-gate    # probabilities:
166*0Sstevel@tonic-gate    #
167*0Sstevel@tonic-gate    #		under 1		68.26% of the time
168*0Sstevel@tonic-gate    #		1-2		27.18% of the time
169*0Sstevel@tonic-gate    #		2-3		 4.30% of the time
170*0Sstevel@tonic-gate    #		over 3		 0.26% of the time
171*0Sstevel@tonic-gate    #
172*0Sstevel@tonic-gate    # If the numbers you see are not scattered approximately
173*0Sstevel@tonic-gate    # (not exactly!) like that table, check with your vendor
174*0Sstevel@tonic-gate    # to find out what's wrong with your rand. Or with this
175*0Sstevel@tonic-gate    # algorithm. :-)
176*0Sstevel@tonic-gate    #
177*0Sstevel@tonic-gate    # Calculating absoulute standard deviation for number of bits set
178*0Sstevel@tonic-gate    # (eight bits per rep)
179*0Sstevel@tonic-gate    $dev = abs ($bits - $reps * 4) / sqrt($reps * 2);
180*0Sstevel@tonic-gate
181*0Sstevel@tonic-gate    ok( $dev < 3.3 );
182*0Sstevel@tonic-gate
183*0Sstevel@tonic-gate    if ($dev < 1.96) {
184*0Sstevel@tonic-gate	print "# Your rand seems fine. If this test failed\n";
185*0Sstevel@tonic-gate	print "# previously, you may want to run it again.\n";
186*0Sstevel@tonic-gate    } elsif ($dev < 2.575) {
187*0Sstevel@tonic-gate	print "# This is ok, but suspicious. But it will happen\n";
188*0Sstevel@tonic-gate	print "# one time out of 25, more or less.\n";
189*0Sstevel@tonic-gate	print "# You should run this test again to be sure.\n";
190*0Sstevel@tonic-gate    } elsif ($dev < 3.3) {
191*0Sstevel@tonic-gate	print "# This is very suspicious. It will happen only\n";
192*0Sstevel@tonic-gate	print "# about one time out of 100, more or less.\n";
193*0Sstevel@tonic-gate	print "# You should run this test again to be sure.\n";
194*0Sstevel@tonic-gate    } elsif ($dev < 3.9) {
195*0Sstevel@tonic-gate	print "# This is VERY suspicious. It will happen only\n";
196*0Sstevel@tonic-gate	print "# about one time out of 1000, more or less.\n";
197*0Sstevel@tonic-gate	print "# You should run this test again to be sure.\n";
198*0Sstevel@tonic-gate    } else {
199*0Sstevel@tonic-gate	print "# This is VERY VERY suspicious.\n";
200*0Sstevel@tonic-gate	print "# Your rand seems to be bogus.\n";
201*0Sstevel@tonic-gate    }
202*0Sstevel@tonic-gate    print "#\n# If you are having random number troubles,\n";
203*0Sstevel@tonic-gate    print "# see the hints within the test script for more\n";
204*0Sstevel@tonic-gate    printf "# information on why this might fail. [ %.3f ]\n", $dev;
205*0Sstevel@tonic-gate}
206*0Sstevel@tonic-gate
207*0Sstevel@tonic-gate
208*0Sstevel@tonic-gate# Now, let's see whether rand accepts its argument
209*0Sstevel@tonic-gate{
210*0Sstevel@tonic-gate    my($max, $min);
211*0Sstevel@tonic-gate    $max = $min = rand(100);
212*0Sstevel@tonic-gate    for (1..$reps) {
213*0Sstevel@tonic-gate	my $n = rand(100);
214*0Sstevel@tonic-gate	$max = $n if $n > $max;
215*0Sstevel@tonic-gate	$min = $n if $n < $min;
216*0Sstevel@tonic-gate    }
217*0Sstevel@tonic-gate
218*0Sstevel@tonic-gate    # This test checks to see that rand(100) really falls
219*0Sstevel@tonic-gate    # within the range 0 - 100, and that the numbers produced
220*0Sstevel@tonic-gate    # have a reasonably-large range among them.
221*0Sstevel@tonic-gate    #
222*0Sstevel@tonic-gate    unless ( ok( !($min < 0 or $max >= 100 or ($max - $min) < 65) ) ) {
223*0Sstevel@tonic-gate	print "# min too low\n" if $min < 0;
224*0Sstevel@tonic-gate	print "# max too high\n" if $max >= 100;
225*0Sstevel@tonic-gate	print "# range too narrow\n" if ($max - $min) < 65;
226*0Sstevel@tonic-gate    }
227*0Sstevel@tonic-gate
228*0Sstevel@tonic-gate
229*0Sstevel@tonic-gate    # This test checks that rand without an argument
230*0Sstevel@tonic-gate    # is equivalent to rand(1).
231*0Sstevel@tonic-gate    #
232*0Sstevel@tonic-gate    $_ = 12345;		# Just for fun.
233*0Sstevel@tonic-gate    srand 12345;
234*0Sstevel@tonic-gate    my $r = rand;
235*0Sstevel@tonic-gate    srand 12345;
236*0Sstevel@tonic-gate    is(rand(1),  $r,  'rand() without args is rand(1)');
237*0Sstevel@tonic-gate
238*0Sstevel@tonic-gate
239*0Sstevel@tonic-gate    # This checks that rand without an argument is not
240*0Sstevel@tonic-gate    # rand($_). (In case somebody got overzealous.)
241*0Sstevel@tonic-gate    #
242*0Sstevel@tonic-gate    ok($r < 1,        'rand() without args is under 1');
243*0Sstevel@tonic-gate}
244*0Sstevel@tonic-gate
245