# Ranlux 24/48 PRNGs. (1.01)
# Chaotic with long period but slow (due to discard).
from collections import deque
from random import choices
class SubtractWithCarryEngine:
def __init__(self, w, s, r):
assert 0 < w
assert 0 < s < r
self.m = 2**w-1
self.s = s
self.r = r
self.x = deque(choices(range(2**w), k=r), maxlen=r)
self.c = 0
def next(self):
x = self.x
i = len(x)
y = x[i - self.s] - x[i - self.r] - self.c
self.c = 1 if y < 0 else 0
x.append(y & self.m)
return x[-1]
def discard(self, n):
for _ in range(n):
self.next()
class DiscardBlockEngine:
def __init__(self, g, p, r):
assert 0 < r <= p
self.g = g
self.p = p
self.r = r
self.n = r
def next(self):
if self.n == 0:
self.g.discard(self.p - self.r)
self.n = self.r
self.n -= 1
return self.g.next()
# Test.
def bitstream(n, prng, bits, *, end='\n', file=None):
assert bits > 0
while n > 0:
m = min(n, bits)
n -= m
r = prng.next()
for i in reversed(range(bits - m, bits)):
print(chr(((r >> i) & 1) + ord('0')), end='', file=file)
print(end=end, file=file)
ranlux24_base = SubtractWithCarryEngine(24, 10, 24)
ranlux24 = DiscardBlockEngine(ranlux24_base, 223, 23)
ranlux48_base = SubtractWithCarryEngine(48, 5, 12)
ranlux48 = DiscardBlockEngine(ranlux48_base, 389, 11)
n = 50
bitstream(n, ranlux24, 24)
bitstream(n, ranlux48, 48)
print('Ranlux24:')
for i in range(24):
print(' ', ranlux24.next())
print('Ranlux48:')
for i in range(12):
print(' ', ranlux48.next())
IyBSYW5sdXggMjQvNDggUFJOR3MuICgxLjAxKQojIENoYW90aWMgd2l0aCBsb25nIHBlcmlvZCBidXQgc2xvdyAoZHVlIHRvIGRpc2NhcmQpLgoKZnJvbSBjb2xsZWN0aW9ucyBpbXBvcnQgZGVxdWUKZnJvbSByYW5kb20gaW1wb3J0IGNob2ljZXMKCmNsYXNzIFN1YnRyYWN0V2l0aENhcnJ5RW5naW5lOgogICAgZGVmIF9faW5pdF9fKHNlbGYsIHcsIHMsIHIpOgogICAgICAgIGFzc2VydCAwIDwgdwogICAgICAgIGFzc2VydCAwIDwgcyA8IHIKICAgICAgICBzZWxmLm0gPSAyKip3LTEKICAgICAgICBzZWxmLnMgPSBzCiAgICAgICAgc2VsZi5yID0gcgogICAgICAgIHNlbGYueCA9IGRlcXVlKGNob2ljZXMocmFuZ2UoMioqdyksIGs9ciksIG1heGxlbj1yKQogICAgICAgIHNlbGYuYyA9IDAKCiAgICBkZWYgbmV4dChzZWxmKToKICAgICAgICB4ID0gc2VsZi54CiAgICAgICAgaSA9IGxlbih4KQogICAgICAgIHkgPSB4W2kgLSBzZWxmLnNdIC0geFtpIC0gc2VsZi5yXSAtIHNlbGYuYwogICAgICAgIHNlbGYuYyA9IDEgaWYgeSA8IDAgZWxzZSAwCiAgICAgICAgeC5hcHBlbmQoeSAmIHNlbGYubSkKICAgICAgICByZXR1cm4geFstMV0KCiAgICBkZWYgZGlzY2FyZChzZWxmLCBuKToKICAgICAgICBmb3IgXyBpbiByYW5nZShuKToKICAgICAgICAgICAgc2VsZi5uZXh0KCkKCmNsYXNzIERpc2NhcmRCbG9ja0VuZ2luZToKICAgIGRlZiBfX2luaXRfXyhzZWxmLCBnLCBwLCByKToKICAgICAgICBhc3NlcnQgMCA8IHIgPD0gcAogICAgICAgIHNlbGYuZyA9IGcKICAgICAgICBzZWxmLnAgPSBwCiAgICAgICAgc2VsZi5yID0gcgogICAgICAgIHNlbGYubiA9IHIKCiAgICBkZWYgbmV4dChzZWxmKToKICAgICAgICBpZiBzZWxmLm4gPT0gMDoKICAgICAgICAgICAgc2VsZi5nLmRpc2NhcmQoc2VsZi5wIC0gc2VsZi5yKQogICAgICAgICAgICBzZWxmLm4gPSBzZWxmLnIKICAgICAgICBzZWxmLm4gLT0gMQogICAgICAgIHJldHVybiBzZWxmLmcubmV4dCgpCgojIFRlc3QuCgpkZWYgYml0c3RyZWFtKG4sIHBybmcsIGJpdHMsICosIGVuZD0nXG4nLCBmaWxlPU5vbmUpOgogICAgYXNzZXJ0IGJpdHMgPiAwCiAgICB3aGlsZSBuID4gMDoKICAgICAgICBtID0gbWluKG4sIGJpdHMpCiAgICAgICAgbiAtPSBtCiAgICAgICAgciA9IHBybmcubmV4dCgpCiAgICAgICAgZm9yIGkgaW4gcmV2ZXJzZWQocmFuZ2UoYml0cyAtIG0sIGJpdHMpKToKICAgICAgICAgICAgcHJpbnQoY2hyKCgociA+PiBpKSAmIDEpICsgb3JkKCcwJykpLCBlbmQ9JycsIGZpbGU9ZmlsZSkKICAgIHByaW50KGVuZD1lbmQsIGZpbGU9ZmlsZSkKCnJhbmx1eDI0X2Jhc2UgPSBTdWJ0cmFjdFdpdGhDYXJyeUVuZ2luZSgyNCwgMTAsIDI0KQpyYW5sdXgyNCA9IERpc2NhcmRCbG9ja0VuZ2luZShyYW5sdXgyNF9iYXNlLCAyMjMsIDIzKQpyYW5sdXg0OF9iYXNlID0gU3VidHJhY3RXaXRoQ2FycnlFbmdpbmUoNDgsIDUsIDEyKQpyYW5sdXg0OCA9IERpc2NhcmRCbG9ja0VuZ2luZShyYW5sdXg0OF9iYXNlLCAzODksIDExKQoKbiA9IDUwCmJpdHN0cmVhbShuLCByYW5sdXgyNCwgMjQpCmJpdHN0cmVhbShuLCByYW5sdXg0OCwgNDgpCgpwcmludCgnUmFubHV4MjQ6JykKZm9yIGkgaW4gcmFuZ2UoMjQpOgogICAgcHJpbnQoJyAnLCByYW5sdXgyNC5uZXh0KCkpCnByaW50KCdSYW5sdXg0ODonKQpmb3IgaSBpbiByYW5nZSgxMik6CiAgICBwcmludCgnICcsIHJhbmx1eDQ4Lm5leHQoKSk=