OLD | NEW |
(Empty) | |
| 1 # -*- coding: utf-8 -*- |
| 2 # |
| 3 # Random/random.py : Strong alternative for the standard 'random' module |
| 4 # |
| 5 # Written in 2008 by Dwayne C. Litzenberger <dlitz@dlitz.net> |
| 6 # |
| 7 # =================================================================== |
| 8 # The contents of this file are dedicated to the public domain. To |
| 9 # the extent that dedication to the public domain is not available, |
| 10 # everyone is granted a worldwide, perpetual, royalty-free, |
| 11 # non-exclusive license to exercise all rights associated with the |
| 12 # contents of this file for any purpose whatsoever. |
| 13 # No rights are reserved. |
| 14 # |
| 15 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, |
| 16 # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF |
| 17 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND |
| 18 # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS |
| 19 # BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN |
| 20 # ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN |
| 21 # CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
| 22 # SOFTWARE. |
| 23 # =================================================================== |
| 24 |
| 25 """A cryptographically strong version of Python's standard "random" module.""" |
| 26 |
| 27 __revision__ = "$Id$" |
| 28 __all__ = ['StrongRandom', 'getrandbits', 'randrange', 'randint', 'choice', 'shu
ffle', 'sample'] |
| 29 |
| 30 from Crypto import Random |
| 31 import sys |
| 32 if sys.version_info[0] == 2 and sys.version_info[1] == 1: |
| 33 from Crypto.Util.py21compat import * |
| 34 |
| 35 class StrongRandom(object): |
| 36 def __init__(self, rng=None, randfunc=None): |
| 37 if randfunc is None and rng is None: |
| 38 self._randfunc = None |
| 39 elif randfunc is not None and rng is None: |
| 40 self._randfunc = randfunc |
| 41 elif randfunc is None and rng is not None: |
| 42 self._randfunc = rng.read |
| 43 else: |
| 44 raise ValueError("Cannot specify both 'rng' and 'randfunc'") |
| 45 |
| 46 def getrandbits(self, k): |
| 47 """Return a python long integer with k random bits.""" |
| 48 if self._randfunc is None: |
| 49 self._randfunc = Random.new().read |
| 50 mask = (1L << k) - 1 |
| 51 return mask & bytes_to_long(self._randfunc(ceil_div(k, 8))) |
| 52 |
| 53 def randrange(self, *args): |
| 54 """randrange([start,] stop[, step]): |
| 55 Return a randomly-selected element from range(start, stop, step).""" |
| 56 if len(args) == 3: |
| 57 (start, stop, step) = args |
| 58 elif len(args) == 2: |
| 59 (start, stop) = args |
| 60 step = 1 |
| 61 elif len(args) == 1: |
| 62 (stop,) = args |
| 63 start = 0 |
| 64 step = 1 |
| 65 else: |
| 66 raise TypeError("randrange expected at most 3 arguments, got %d" % (
len(args),)) |
| 67 if (not isinstance(start, (int, long)) |
| 68 or not isinstance(stop, (int, long)) |
| 69 or not isinstance(step, (int, long))): |
| 70 raise TypeError("randrange requires integer arguments") |
| 71 if step == 0: |
| 72 raise ValueError("randrange step argument must not be zero") |
| 73 |
| 74 num_choices = ceil_div(stop - start, step) |
| 75 if num_choices < 0: |
| 76 num_choices = 0 |
| 77 if num_choices < 1: |
| 78 raise ValueError("empty range for randrange(%r, %r, %r)" % (start, s
top, step)) |
| 79 |
| 80 # Pick a random number in the range of possible numbers |
| 81 r = num_choices |
| 82 while r >= num_choices: |
| 83 r = self.getrandbits(size(num_choices)) |
| 84 |
| 85 return start + (step * r) |
| 86 |
| 87 def randint(self, a, b): |
| 88 """Return a random integer N such that a <= N <= b.""" |
| 89 if not isinstance(a, (int, long)) or not isinstance(b, (int, long)): |
| 90 raise TypeError("randint requires integer arguments") |
| 91 N = self.randrange(a, b+1) |
| 92 assert a <= N <= b |
| 93 return N |
| 94 |
| 95 def choice(self, seq): |
| 96 """Return a random element from a (non-empty) sequence. |
| 97 |
| 98 If the seqence is empty, raises IndexError. |
| 99 """ |
| 100 if len(seq) == 0: |
| 101 raise IndexError("empty sequence") |
| 102 return seq[self.randrange(len(seq))] |
| 103 |
| 104 def shuffle(self, x): |
| 105 """Shuffle the sequence in place.""" |
| 106 # Make a (copy) of the list of objects we want to shuffle |
| 107 items = list(x) |
| 108 |
| 109 # Choose a random item (without replacement) until all the items have be
en |
| 110 # chosen. |
| 111 for i in xrange(len(x)): |
| 112 x[i] = items.pop(self.randrange(len(items))) |
| 113 |
| 114 def sample(self, population, k): |
| 115 """Return a k-length list of unique elements chosen from the population
sequence.""" |
| 116 |
| 117 num_choices = len(population) |
| 118 if k > num_choices: |
| 119 raise ValueError("sample larger than population") |
| 120 |
| 121 retval = [] |
| 122 selected = {} # we emulate a set using a dict here |
| 123 for i in xrange(k): |
| 124 r = None |
| 125 while r is None or selected.has_key(r): |
| 126 r = self.randrange(num_choices) |
| 127 retval.append(population[r]) |
| 128 selected[r] = 1 |
| 129 return retval |
| 130 |
| 131 _r = StrongRandom() |
| 132 getrandbits = _r.getrandbits |
| 133 randrange = _r.randrange |
| 134 randint = _r.randint |
| 135 choice = _r.choice |
| 136 shuffle = _r.shuffle |
| 137 sample = _r.sample |
| 138 |
| 139 # These are at the bottom to avoid problems with recursive imports |
| 140 from Crypto.Util.number import ceil_div, bytes_to_long, long_to_bytes, size |
| 141 |
| 142 # vim:set ts=4 sw=4 sts=4 expandtab: |
OLD | NEW |