# Ranlux 24/48 PRNGs
# 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)
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())
IyBSYW5sdXggMjQvNDggUFJOR3MKIyBDaGFvdGljIHdpdGggbG9uZyBwZXJpb2QgYnV0IHNsb3cgKGR1ZSB0byBkaXNjYXJkKS4KCmZyb20gY29sbGVjdGlvbnMgaW1wb3J0IGRlcXVlCmZyb20gcmFuZG9tIGltcG9ydCBjaG9pY2VzCgpjbGFzcyBTdWJ0cmFjdFdpdGhDYXJyeUVuZ2luZToKICAgIGRlZiBfX2luaXRfXyhzZWxmLCB3LCBzLCByKToKICAgICAgICBhc3NlcnQgMCA8IHcKICAgICAgICBhc3NlcnQgMCA8IHMgPCByCiAgICAgICAgc2VsZi5tID0gMioqdy0xCiAgICAgICAgc2VsZi5zID0gcwogICAgICAgIHNlbGYuciA9IHIKICAgICAgICBzZWxmLnggPSBkZXF1ZShjaG9pY2VzKHJhbmdlKDIqKncpLCBrPXIpLCBtYXhsZW49cikKICAgICAgICBzZWxmLmMgPSAwCgogICAgZGVmIG5leHQoc2VsZik6CiAgICAgICAgeCA9IHNlbGYueAogICAgICAgIGkgPSBsZW4oeCkKICAgICAgICB5ID0geFtpIC0gc2VsZi5zXSAtIHhbaSAtIHNlbGYucl0gLSBzZWxmLmMKICAgICAgICBzZWxmLmMgPSAxIGlmIHkgPCAwIGVsc2UgMAogICAgICAgIHguYXBwZW5kKHkgJiBzZWxmLm0pCiAgICAgICAgcmV0dXJuIHhbLTFdCgogICAgZGVmIGRpc2NhcmQoc2VsZiwgbik6CiAgICAgICAgZm9yIF8gaW4gcmFuZ2Uobik6CiAgICAgICAgICAgIHNlbGYubmV4dCgpCgpjbGFzcyBEaXNjYXJkQmxvY2tFbmdpbmU6CiAgICBkZWYgX19pbml0X18oc2VsZiwgZywgcCwgcik6CiAgICAgICAgYXNzZXJ0IDAgPCByIDw9IHAKICAgICAgICBzZWxmLmcgPSBnCiAgICAgICAgc2VsZi5wID0gcAogICAgICAgIHNlbGYuciA9IHIKICAgICAgICBzZWxmLm4gPSByCgogICAgZGVmIG5leHQoc2VsZik6CiAgICAgICAgaWYgc2VsZi5uID09IDA6CiAgICAgICAgICAgIHNlbGYuZy5kaXNjYXJkKHNlbGYucCAtIHNlbGYucikKICAgICAgICAgICAgc2VsZi5uID0gc2VsZi5yCiAgICAgICAgc2VsZi5uIC09IDEKICAgICAgICByZXR1cm4gc2VsZi5nLm5leHQoKQoKIyBUZXN0LgoKZGVmIGJpdHN0cmVhbShuLCBwcm5nLCBiaXRzLCAqLCBlbmQ9J1xuJywgZmlsZT1Ob25lKToKICAgIGFzc2VydCBiaXRzID4gMAogICAgd2hpbGUgbiA+IDA6CiAgICAgICAgbSA9IG1pbihuLCBiaXRzKQogICAgICAgIG4gLT0gbQogICAgICAgIHIgPSBwcm5nLm5leHQoKQogICAgICAgIGZvciBpIGluIHJldmVyc2VkKHJhbmdlKGJpdHMgLSBtLCBiaXRzKSk6CiAgICAgICAgICAgIHByaW50KGNocigoKHIgPj4gaSkgJiAxKSArIG9yZCgnMCcpKSwgZW5kPScnLCBmaWxlPWZpbGUpCiAgICBwcmludChlbmQ9ZW5kKQoKcmFubHV4MjRfYmFzZSA9IFN1YnRyYWN0V2l0aENhcnJ5RW5naW5lKDI0LCAxMCwgMjQpCnJhbmx1eDI0ID0gRGlzY2FyZEJsb2NrRW5naW5lKHJhbmx1eDI0X2Jhc2UsIDIyMywgMjMpCnJhbmx1eDQ4X2Jhc2UgPSBTdWJ0cmFjdFdpdGhDYXJyeUVuZ2luZSg0OCwgNSwgMTIpCnJhbmx1eDQ4ID0gRGlzY2FyZEJsb2NrRW5naW5lKHJhbmx1eDQ4X2Jhc2UsIDM4OSwgMTEpCgpuID0gNTAKYml0c3RyZWFtKG4sIHJhbmx1eDI0LCAyNCkKYml0c3RyZWFtKG4sIHJhbmx1eDQ4LCA0OCkKCnByaW50KCdSYW5sdXgyNDonKQpmb3IgaSBpbiByYW5nZSgyNCk6CiAgICBwcmludCgnICcsIHJhbmx1eDI0Lm5leHQoKSkKcHJpbnQoJ1Jhbmx1eDQ4OicpCmZvciBpIGluIHJhbmdlKDEyKToKICAgIHByaW50KCcgJywgcmFubHV4NDgubmV4dCgpKQ==