#!/usr/bin/env python3 import unittest import random import time import pickle import warnings from math import log, exp, pi, fsum, sin from test import support class TestBasicOps(unittest.TestCase): # Superclass with tests common to all generators. # Subclasses must arrange for self.gen to retrieve the Random instance # to be tested. def randomlist(self, n): """Helper function to make a list of random numbers""" return [self.gen.random() for i in range(n)] def test_autoseed(self): self.gen.seed() state1 = self.gen.getstate() time.sleep(0.1) self.gen.seed() # diffent seeds at different times state2 = self.gen.getstate() self.assertNotEqual(state1, state2) def test_saverestore(self): N = 1000 self.gen.seed() state = self.gen.getstate() randseq = self.randomlist(N) self.gen.setstate(state) # should regenerate the same sequence self.assertEqual(randseq, self.randomlist(N)) def test_seedargs(self): # Seed value with a negative hash. class MySeed(object): def __hash__(self): return -1729 for arg in [None, 0, 0, 1, 1, -1, -1, 10**20, -(10**20), 3.14, 1+2j, 'a', tuple('abc'), MySeed()]: self.gen.seed(arg) for arg in [list(range(3)), dict(one=1)]: self.assertRaises(TypeError, self.gen.seed, arg) self.assertRaises(TypeError, self.gen.seed, 1, 2, 3, 4) self.assertRaises(TypeError, type(self.gen), []) def test_shuffle(self): shuffle = self.gen.shuffle lst = [] shuffle(lst) self.assertEqual(lst, []) lst = [37] shuffle(lst) self.assertEqual(lst, [37]) seqs = [list(range(n)) for n in range(10)] shuffled_seqs = [list(range(n)) for n in range(10)] for shuffled_seq in shuffled_seqs: shuffle(shuffled_seq) for (seq, shuffled_seq) in zip(seqs, shuffled_seqs): self.assertEqual(len(seq), len(shuffled_seq)) self.assertEqual(set(seq), set(shuffled_seq)) # The above tests all would pass if the shuffle was a # no-op. The following non-deterministic test covers that. It # asserts that the shuffled sequence of 1000 distinct elements # must be different from the original one. Although there is # mathematically a non-zero probability that this could # actually happen in a genuinely random shuffle, it is # completely negligible, given that the number of possible # permutations of 1000 objects is 1000! (factorial of 1000), # which is considerably larger than the number of atoms in the # universe... lst = list(range(1000)) shuffled_lst = list(range(1000)) shuffle(shuffled_lst) self.assertTrue(lst != shuffled_lst) shuffle(lst) self.assertTrue(lst != shuffled_lst) def test_choice(self): choice = self.gen.choice with self.assertRaises(IndexError): choice([]) self.assertEqual(choice([50]), 50) self.assertIn(choice([25, 75]), [25, 75]) def test_sample(self): # For the entire allowable range of 0 <= k <= N, validate that # the sample is of the correct length and contains only unique items N = 100 population = range(N) for k in range(N+1): s = self.gen.sample(population, k) self.assertEqual(len(s), k) uniq = set(s) self.assertEqual(len(uniq), k) self.assertTrue(uniq <= set(population)) self.assertEqual(self.gen.sample([], 0), []) # test edge case N==k==0 def test_sample_distribution(self): # For the entire allowable range of 0 <= k <= N, validate that # sample generates all possible permutations n = 5 pop = range(n) trials = 10000 # large num prevents false negatives without slowing normal case def factorial(n): if n == 0: return 1 return n * factorial(n - 1) for k in range(n): expected = factorial(n) // factorial(n-k) perms = {} for i in range(trials): perms[tuple(self.gen.sample(pop, k))] = None if len(perms) == expected: break else: self.fail() def test_sample_inputs(self): # SF bug #801342 -- population can be any iterable defining __len__() self.gen.sample(set(range(20)), 2) self.gen.sample(range(20), 2) self.gen.sample(range(20), 2) self.gen.sample(str('abcdefghijklmnopqrst'), 2) self.gen.sample(tuple('abcdefghijklmnopqrst'), 2) def test_sample_on_dicts(self): self.assertRaises(TypeError, self.gen.sample, dict.fromkeys('abcdef'), 2) def test_gauss(self): # Ensure that the seed() method initializes all the hidden state. In # particular, through 2.2.1 it failed to reset a piece of state used # by (and only by) the .gauss() method. for seed in 1, 12, 123, 1234, 12345, 123456, 654321: self.gen.seed(seed) x1 = self.gen.random() y1 = self.gen.gauss(0, 1) self.gen.seed(seed) x2 = self.gen.random() y2 = self.gen.gauss(0, 1) self.assertEqual(x1, x2) self.assertEqual(y1, y2) def test_pickling(self): state = pickle.dumps(self.gen) origseq = [self.gen.random() for i in range(10)] newgen = pickle.loads(state) restoredseq = [newgen.random() for i in range(10)] self.assertEqual(origseq, restoredseq) def test_bug_1727780(self): # verify that version-2-pickles can be loaded # fine, whether they are created on 32-bit or 64-bit # platforms, and that version-3-pickles load fine. files = [("randv2_32.pck", 780), ("randv2_64.pck", 866), ("randv3.pck", 343)] for file, value in files: f = open(support.findfile(file),"rb") r = pickle.load(f) f.close() self.assertEqual(int(r.random()*1000), value) def test_bug_9025(self): # Had problem with an uneven distribution in int(n*random()) # Verify the fix by checking that distributions fall within expectations. n = 100000 randrange = self.gen.randrange k = sum(randrange(6755399441055744) % 3 == 2 for i in range(n)) self.assertTrue(0.30 < k/n < .37, (k/n)) class SystemRandom_TestBasicOps(TestBasicOps): gen = random.SystemRandom() def test_autoseed(self): # Doesn't need to do anything except not fail self.gen.seed() def test_saverestore(self): self.assertRaises(NotImplementedError, self.gen.getstate) self.assertRaises(NotImplementedError, self.gen.setstate, None) def test_seedargs(self): # Doesn't need to do anything except not fail self.gen.seed(100) def test_gauss(self): self.gen.gauss_next = None self.gen.seed(100) self.assertEqual(self.gen.gauss_next, None) def test_pickling(self): self.assertRaises(NotImplementedError, pickle.dumps, self.gen) def test_53_bits_per_float(self): # This should pass whenever a C double has 53 bit precision. span = 2 ** 53 cum = 0 for i in range(100): cum |= int(self.gen.random() * span) self.assertEqual(cum, span-1) def test_bigrand(self): # The randrange routine should build-up the required number of bits # in stages so that all bit positions are active. span = 2 ** 500 cum = 0 for i in range(100): r = self.gen.randrange(span) self.assertTrue(0 <= r < span) cum |= r self.assertEqual(cum, span-1) def test_bigrand_ranges(self): for i in [40,80, 160, 200, 211, 250, 375, 512, 550]: start = self.gen.randrange(2 ** i) stop = self.gen.randrange(2 ** (i-2)) if stop <= start: return self.assertTrue(start <= self.gen.randrange(start, stop) < stop) def test_rangelimits(self): for start, stop in [(-2,0), (-(2**60)-2,-(2**60)), (2**60,2**60+2)]: self.assertEqual(set(range(start,stop)), set([self.gen.randrange(start,stop) for i in range(100)])) def test_genrandbits(self): # Verify ranges for k in range(1, 1000): self.assertTrue(0 <= self.gen.getrandbits(k) < 2**k) # Verify all bits active getbits = self.gen.getrandbits for span in [1, 2, 3, 4, 31, 32, 32, 52, 53, 54, 119, 127, 128, 129]: cum = 0 for i in range(100): cum |= getbits(span) self.assertEqual(cum, 2**span-1) # Verify argument checking self.assertRaises(TypeError, self.gen.getrandbits) self.assertRaises(TypeError, self.gen.getrandbits, 1, 2) self.assertRaises(ValueError, self.gen.getrandbits, 0) self.assertRaises(ValueError, self.gen.getrandbits, -1) self.assertRaises(TypeError, self.gen.getrandbits, 10.1) def test_randbelow_logic(self, _log=log, int=int): # check bitcount transition points: 2**i and 2**(i+1)-1 # show that: k = int(1.001 + _log(n, 2)) # is equal to or one greater than the number of bits in n for i in range(1, 1000): n = 1 << i # check an exact power of two numbits = i+1 k = int(1.00001 + _log(n, 2)) self.assertEqual(k, numbits) self.assertEqual(n, 2**(k-1)) n += n - 1 # check 1 below the next power of two k = int(1.00001 + _log(n, 2)) self.assertIn(k, [numbits, numbits+1]) self.assertTrue(2**k > n > 2**(k-2)) n -= n >> 15 # check a little farther below the next power of two k = int(1.00001 + _log(n, 2)) self.assertEqual(k, numbits) # note the stronger assertion self.assertTrue(2**k > n > 2**(k-1)) # note the stronger assertion class MersenneTwister_TestBasicOps(TestBasicOps): gen = random.Random() def test_guaranteed_stable(self): # These sequences are guaranteed to stay the same across versions of python self.gen.seed(3456147, version=1) self.assertEqual([self.gen.random().hex() for i in range(4)], ['0x1.ac362300d90d2p-1', '0x1.9d16f74365005p-1', '0x1.1ebb4352e4c4dp-1', '0x1.1a7422abf9c11p-1']) self.gen.seed("the quick brown fox", version=2) self.assertEqual([self.gen.random().hex() for i in range(4)], ['0x1.1239ddfb11b7cp-3', '0x1.b3cbb5c51b120p-4', '0x1.8c4f55116b60fp-1', '0x1.63eb525174a27p-1']) def test_setstate_first_arg(self): self.assertRaises(ValueError, self.gen.setstate, (1, None, None)) def test_setstate_middle_arg(self): # Wrong type, s/b tuple self.assertRaises(TypeError, self.gen.setstate, (2, None, None)) # Wrong length, s/b 625 self.assertRaises(ValueError, self.gen.setstate, (2, (1,2,3), None)) # Wrong type, s/b tuple of 625 ints self.assertRaises(TypeError, self.gen.setstate, (2, ('a',)*625, None)) # Last element s/b an int also self.assertRaises(TypeError, self.gen.setstate, (2, (0,)*624+('a',), None)) def test_referenceImplementation(self): # Compare the python implementation with results from the original # code. Create 2000 53-bit precision random floats. Compare only # the last ten entries to show that the independent implementations # are tracking. Here is the main() function needed to create the # list of expected random numbers: # void main(void){ # int i; # unsigned long init[4]={61731, 24903, 614, 42143}, length=4; # init_by_array(init, length); # for (i=0; i<2000; i++) { # printf("%.15f ", genrand_res53()); # if (i%5==4) printf("\n"); # } # } expected = [0.45839803073713259, 0.86057815201978782, 0.92848331726782152, 0.35932681119782461, 0.081823493762449573, 0.14332226470169329, 0.084297823823520024, 0.53814864671831453, 0.089215024911993401, 0.78486196105372907] self.gen.seed(61731 + (24903<<32) + (614<<64) + (42143<<96)) actual = self.randomlist(2000)[-10:] for a, e in zip(actual, expected): self.assertAlmostEqual(a,e,places=14) def test_strong_reference_implementation(self): # Like test_referenceImplementation, but checks for exact bit-level # equality. This should pass on any box where C double contains # at least 53 bits of precision (the underlying algorithm suffers # no rounding errors -- all results are exact). from math import ldexp expected = [0x0eab3258d2231f, 0x1b89db315277a5, 0x1db622a5518016, 0x0b7f9af0d575bf, 0x029e4c4db82240, 0x04961892f5d673, 0x02b291598e4589, 0x11388382c15694, 0x02dad977c9e1fe, 0x191d96d4d334c6] self.gen.seed(61731 + (24903<<32) + (614<<64) + (42143<<96)) actual = self.randomlist(2000)[-10:] for a, e in zip(actual, expected): self.assertEqual(int(ldexp(a, 53)), e) def test_long_seed(self): # This is most interesting to run in debug mode, just to make sure # nothing blows up. Under the covers, a dynamically resized array # is allocated, consuming space proportional to the number of bits # in the seed. Unfortunately, that's a quadratic-time algorithm, # so don't make this horribly big. seed = (1 << (10000 * 8)) - 1 # about 10K bytes self.gen.seed(seed) def test_53_bits_per_float(self): # This should pass whenever a C double has 53 bit precision. span = 2 ** 53 cum = 0 for i in range(100): cum |= int(self.gen.random() * span) self.assertEqual(cum, span-1) def test_bigrand(self): # The randrange routine should build-up the required number of bits # in stages so that all bit positions are active. span = 2 ** 500 cum = 0 for i in range(100): r = self.gen.randrange(span) self.assertTrue(0 <= r < span) cum |= r self.assertEqual(cum, span-1) def test_bigrand_ranges(self): for i in [40,80, 160, 200, 211, 250, 375, 512, 550]: start = self.gen.randrange(2 ** i) stop = self.gen.randrange(2 ** (i-2)) if stop <= start: return self.assertTrue(start <= self.gen.randrange(start, stop) < stop) def test_rangelimits(self): for start, stop in [(-2,0), (-(2**60)-2,-(2**60)), (2**60,2**60+2)]: self.assertEqual(set(range(start,stop)), set([self.gen.randrange(start,stop) for i in range(100)])) def test_genrandbits(self): # Verify cross-platform repeatability self.gen.seed(1234567) self.assertEqual(self.gen.getrandbits(100), 97904845777343510404718956115) # Verify ranges for k in range(1, 1000): self.assertTrue(0 <= self.gen.getrandbits(k) < 2**k) # Verify all bits active getbits = self.gen.getrandbits for span in [1, 2, 3, 4, 31, 32, 32, 52, 53, 54, 119, 127, 128, 129]: cum = 0 for i in range(100): cum |= getbits(span) self.assertEqual(cum, 2**span-1) # Verify argument checking self.assertRaises(TypeError, self.gen.getrandbits) self.assertRaises(TypeError, self.gen.getrandbits, 'a') self.assertRaises(TypeError, self.gen.getrandbits, 1, 2) self.assertRaises(ValueError, self.gen.getrandbits, 0) self.assertRaises(ValueError, self.gen.getrandbits, -1) def test_randbelow_logic(self, _log=log, int=int): # check bitcount transition points: 2**i and 2**(i+1)-1 # show that: k = int(1.001 + _log(n, 2)) # is equal to or one greater than the number of bits in n for i in range(1, 1000): n = 1 << i # check an exact power of two numbits = i+1 k = int(1.00001 + _log(n, 2)) self.assertEqual(k, numbits) self.assertEqual(n, 2**(k-1)) n += n - 1 # check 1 below the next power of two k = int(1.00001 + _log(n, 2)) self.assertIn(k, [numbits, numbits+1]) self.assertTrue(2**k > n > 2**(k-2)) n -= n >> 15 # check a little farther below the next power of two k = int(1.00001 + _log(n, 2)) self.assertEqual(k, numbits) # note the stronger assertion self.assertTrue(2**k > n > 2**(k-1)) # note the stronger assertion def test_randrange_bug_1590891(self): start = 1000000000000 stop = -100000000000000000000 step = -200 x = self.gen.randrange(start, stop, step) self.assertTrue(stop < x <= start) self.assertEqual((x+stop)%step, 0) def gamma(z, sqrt2pi=(2.0*pi)**0.5): # Reflection to right half of complex plane if z < 0.5: return pi / sin(pi*z) / gamma(1.0-z) # Lanczos approximation with g=7 az = z + (7.0 - 0.5) return az ** (z-0.5) / exp(az) * sqrt2pi * fsum([ 0.9999999999995183, 676.5203681218835 / z, -1259.139216722289 / (z+1.0), 771.3234287757674 / (z+2.0), -176.6150291498386 / (z+3.0), 12.50734324009056 / (z+4.0), -0.1385710331296526 / (z+5.0), 0.9934937113930748e-05 / (z+6.0), 0.1659470187408462e-06 / (z+7.0), ]) class TestDistributions(unittest.TestCase): def test_zeroinputs(self): # Verify that distributions can handle a series of zero inputs' g = random.Random() x = [g.random() for i in range(50)] + [0.0]*5 g.random = x[:].pop; g.uniform(1,10) g.random = x[:].pop; g.paretovariate(1.0) g.random = x[:].pop; g.expovariate(1.0) g.random = x[:].pop; g.weibullvariate(1.0, 1.0) g.random = x[:].pop; g.normalvariate(0.0, 1.0) g.random = x[:].pop; g.gauss(0.0, 1.0) g.random = x[:].pop; g.lognormvariate(0.0, 1.0) g.random = x[:].pop; g.vonmisesvariate(0.0, 1.0) g.random = x[:].pop; g.gammavariate(0.01, 1.0) g.random = x[:].pop; g.gammavariate(1.0, 1.0) g.random = x[:].pop; g.gammavariate(200.0, 1.0) g.random = x[:].pop; g.betavariate(3.0, 3.0) g.random = x[:].pop; g.triangular(0.0, 1.0, 1.0/3.0) def test_avg_std(self): # Use integration to test distribution average and standard deviation. # Only works for distributions which do not consume variates in pairs g = random.Random() N = 5000 x = [i/float(N) for i in range(1,N)] for variate, args, mu, sigmasqrd in [ (g.uniform, (1.0,10.0), (10.0+1.0)/2, (10.0-1.0)**2/12), (g.triangular, (0.0, 1.0, 1.0/3.0), 4.0/9.0, 7.0/9.0/18.0), (g.expovariate, (1.5,), 1/1.5, 1/1.5**2), (g.paretovariate, (5.0,), 5.0/(5.0-1), 5.0/((5.0-1)**2*(5.0-2))), (g.weibullvariate, (1.0, 3.0), gamma(1+1/3.0), gamma(1+2/3.0)-gamma(1+1/3.0)**2) ]: g.random = x[:].pop y = [] for i in range(len(x)): try: y.append(variate(*args)) except IndexError: pass s1 = s2 = 0 for e in y: s1 += e s2 += (e - mu) ** 2 N = len(y) self.assertAlmostEqual(s1/N, mu, places=2) self.assertAlmostEqual(s2/(N-1), sigmasqrd, places=2) class TestModule(unittest.TestCase): def testMagicConstants(self): self.assertAlmostEqual(random.NV_MAGICCONST, 1.71552776992141) self.assertAlmostEqual(random.TWOPI, 6.28318530718) self.assertAlmostEqual(random.LOG4, 1.38629436111989) self.assertAlmostEqual(random.SG_MAGICCONST, 2.50407739677627) def test__all__(self): # tests validity but not completeness of the __all__ list self.assertTrue(set(random.__all__) <= set(dir(random))) def test_random_subclass_with_kwargs(self): # SF bug #1486663 -- this used to erroneously raise a TypeError class Subclass(random.Random): def __init__(self, newarg=None): random.Random.__init__(self) Subclass(newarg=1) def test_main(verbose=None): testclasses = [MersenneTwister_TestBasicOps, TestDistributions, TestModule] try: random.SystemRandom().random() except NotImplementedError: pass else: testclasses.append(SystemRandom_TestBasicOps) support.run_unittest(*testclasses) # verify reference counting import sys if verbose and hasattr(sys, "gettotalrefcount"): counts = [None] * 5 for i in range(len(counts)): support.run_unittest(*testclasses) counts[i] = sys.gettotalrefcount() print(counts) if __name__ == "__main__": test_main(verbose=True)