Source code for mt19937predictor

import random
import sys

# compatibility
if sys.version_info[0] == 2:
    def _to_bytes(n, length, byteorder):
        assert byteorder == 'little'
        return ('%x' % n).zfill(length * 2).decode('hex')[: : -1]
    def _from_bytes(s, byteorder):
        assert byteorder == 'little'
        return int(str(s[: : -1]).encode('hex'), 16)
else:
    _to_bytes = lambda n, *args, **kwargs: n.to_bytes(*args, **kwargs)
    _from_bytes = lambda *args, **kwargs: int.from_bytes(*args, **kwargs)

N = 624  #: 624 values (of 32bit) is just enough to reconstruct the internal state
M = 397  #:
MATRIX_A   = 0x9908b0df  #:
UPPER_MASK = 0x80000000  #:
LOWER_MASK = 0x7fffffff  #:

[docs]def tempering(y): y ^= (y >> 11) y ^= (y << 7) & 0x9d2c5680 y ^= (y << 15) & 0xefc60000 y ^= (y >> 18) return y
[docs]def untempering(y): y ^= (y >> 18) y ^= (y << 15) & 0xefc60000 y ^= ((y << 7) & 0x9d2c5680) ^ ((y << 14) & 0x94284000) ^ ((y << 21) & 0x14200000) ^ ((y << 28) & 0x10000000) y ^= (y >> 11) ^ (y >> 22) return y
[docs]def generate(mt, kk): mag01 = [0x0, MATRIX_A] y = (mt[kk] & UPPER_MASK) | (mt[(kk + 1) % N] & LOWER_MASK) mt[kk] = mt[(kk + M) % N] ^ (y >> 1) ^ mag01[y & 0x1]
[docs]def genrand_int32(mt, mti): generate(mt, mti) y = mt[mti] mti = (mti + 1) % N return tempering(y), mti
[docs]class MT19937Predictor(random.Random): ''' Usage: .. doctest:: >>> import random >>> from mt19937predictor import MT19937Predictor >>> predictor = MT19937Predictor() >>> for _ in range(624): ... x = random.getrandbits(32) ... predictor.setrandbits(x, 32) >>> random.getrandbits(32) == predictor.getrandbits(32) True >>> random.random() == predictor.random() True >>> a = list(range(100)) >>> b = list(range(100)) >>> random.shuffle(a) >>> predictor.shuffle(b) >>> a == b True ''' def __init__(self): self._mt = [ 0 ] * N self._mti = 0
[docs] def setrand_int32(self, y): '''Feceive the target PRNG's outputs and reconstruct the inner state. when 624 consecutive DOWRDs is given, the inner state is uniquely determined. ''' assert 0 <= y < 2 ** 32 self._mt[self._mti] = untempering(y) self._mti = (self._mti + 1) % N
[docs] def genrand_int32(self): y, self._mti = genrand_int32(self._mt, self._mti) return y
[docs] def setrandbits(self, y, bits): '''The interface for :py:meth:`random.Random.getrandbits` in Python's Standard Library ''' if not (bits % 32 == 0): raise ValueError('number of bits must be a multiple of 32') if not (0 <= y < 2 ** bits): raise ValueError('invalid state') if bits == 32: self.setrand_int32(y) else: while bits > 0: self.setrand_int32(y & 0xffffffff) y >>= 32 bits -= 32
[docs] def getrandbits(self, bits): '''The interface for :py:meth:`random.Random.getrandbits` in Python's Standard Library ''' if not (bits > 0): raise ValueError('number of bits must be greater than zero') if bits <= 32: return self.genrand_int32() >> (32 - bits) else: acc = bytearray() while bits > 0: r = self.genrand_int32() if bits < 32: r >>= 32 - bits acc += _to_bytes(r, 4, byteorder='little') bits -= 32 return _from_bytes(acc, byteorder='little')
[docs] def random(self): '''The interface for :py:meth:`random.Random.random` in Python's Standard Library ''' a = self.genrand_int32() >> 5 b = self.genrand_int32() >> 6 return ((a * 67108864.0 + b) * (1.0 / 9007199254740992.0))
[docs] def seed(self, *args): ''' Raises: :py:exc:`NotImplementedError` ''' raise NotImplementedError
[docs] def setstate(self, *args): ''' Raises: :py:exc:`NotImplementedError` ''' raise NotImplementedError
[docs] def getstate(self, *args): ''' Raises: :py:exc:`NotImplementedError` ''' raise NotImplementedError
[docs] def gauss(self, *args): ''' Raises: :py:exc:`NotImplementedError` ''' raise NotImplementedError