multirandom 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- multirandom-0.0.1.dist-info/METADATA +101 -0
- multirandom-0.0.1.dist-info/RECORD +17 -0
- multirandom-0.0.1.dist-info/WHEEL +5 -0
- multirandom-0.0.1.dist-info/licenses/LICENSE +674 -0
- multirandom-0.0.1.dist-info/top_level.txt +1 -0
- realrandom/__init__.py +3 -0
- realrandom/hash_rand/__init__.py +2 -0
- realrandom/hash_rand/original_random.py +1 -0
- realrandom/hash_rand/sha_rand.py +86 -0
- realrandom/true_rand/__init__.py +3 -0
- realrandom/true_rand/rand_using_online_api.py +74 -0
- realrandom/true_rand/rand_via_clicks.py +93 -0
- realrandom/true_rand/weather_rand.py +2 -0
- realrandom/virt_rand/__init__.py +2 -0
- realrandom/virt_rand/flash_rand_and_reverse.py +1 -0
- realrandom/virt_rand/rand_using_virt_lcg.py +93 -0
- realrandom/virt_rand/xor_shift.py +235 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
from random import *
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
_sha1 = None
|
|
2
|
+
_sha224 = None
|
|
3
|
+
_sha256 = None
|
|
4
|
+
_sha384 = None
|
|
5
|
+
_sha512 = None
|
|
6
|
+
|
|
7
|
+
def _get_sha1():
|
|
8
|
+
global _sha1
|
|
9
|
+
if _sha1 is None:
|
|
10
|
+
try:
|
|
11
|
+
from _sha1 import sha1 as _sha1
|
|
12
|
+
except ImportError:
|
|
13
|
+
from hashlib import sha1 as _sha1
|
|
14
|
+
return _sha1
|
|
15
|
+
|
|
16
|
+
def _get_sha224():
|
|
17
|
+
global _sha224
|
|
18
|
+
if _sha224 is None:
|
|
19
|
+
try:
|
|
20
|
+
from _sha2 import sha224 as _sha224
|
|
21
|
+
except ImportError:
|
|
22
|
+
from hashlib import sha224 as _sha224
|
|
23
|
+
return _sha224
|
|
24
|
+
|
|
25
|
+
def _get_sha256():
|
|
26
|
+
global _sha256
|
|
27
|
+
if _sha256 is None:
|
|
28
|
+
try:
|
|
29
|
+
from _sha2 import sha256 as _sha256
|
|
30
|
+
except ImportError:
|
|
31
|
+
from hashlib import sha256 as _sha256
|
|
32
|
+
return _sha256
|
|
33
|
+
|
|
34
|
+
def _get_sha384():
|
|
35
|
+
global _sha384
|
|
36
|
+
if _sha384 is None:
|
|
37
|
+
try:
|
|
38
|
+
from _sha2 import sha384 as _sha384
|
|
39
|
+
except ImportError:
|
|
40
|
+
from hashlib import sha384 as _sha384
|
|
41
|
+
return _sha384
|
|
42
|
+
|
|
43
|
+
def _get_sha512():
|
|
44
|
+
global _sha512
|
|
45
|
+
if _sha512 is None:
|
|
46
|
+
try:
|
|
47
|
+
# hashlib is pretty heavy to load, try lean internal module first
|
|
48
|
+
from _sha2 import sha512 as _sha512
|
|
49
|
+
except ImportError:
|
|
50
|
+
# fallback to official implementation
|
|
51
|
+
from hashlib import sha512 as _sha512
|
|
52
|
+
return _sha512
|
|
53
|
+
class _SHARandom:
|
|
54
|
+
def __init__(self, seed=None):
|
|
55
|
+
self.seed = seed
|
|
56
|
+
self.counter = 0
|
|
57
|
+
|
|
58
|
+
def _get_hash(self):
|
|
59
|
+
raise NotImplementedError
|
|
60
|
+
|
|
61
|
+
def getrandbytes(self, n):
|
|
62
|
+
res = b""
|
|
63
|
+
hf = self._get_hash()
|
|
64
|
+
seed_bytes = str(self.seed).encode()
|
|
65
|
+
while len(res) < n:
|
|
66
|
+
h = hf()
|
|
67
|
+
h.update(seed_bytes + self.counter.to_bytes(8, 'big'))
|
|
68
|
+
res += h.digest()
|
|
69
|
+
self.counter += 1
|
|
70
|
+
return res[:n]
|
|
71
|
+
|
|
72
|
+
class SHA1Random(_SHARandom):
|
|
73
|
+
def _get_hash(self):
|
|
74
|
+
return _get_sha1()
|
|
75
|
+
|
|
76
|
+
class SHA256Random(_SHARandom):
|
|
77
|
+
def _get_hash(self):
|
|
78
|
+
return _get_sha256()
|
|
79
|
+
|
|
80
|
+
class SHA384Random(_SHARandom):
|
|
81
|
+
def _get_hash(self):
|
|
82
|
+
return _get_sha384()
|
|
83
|
+
|
|
84
|
+
class SHA512Random(_SHARandom):
|
|
85
|
+
def _get_hash(self):
|
|
86
|
+
return _get_sha512()
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import aiohttp
|
|
2
|
+
import asyncio
|
|
3
|
+
|
|
4
|
+
async def fetch_random_org_integers(num: int = 1, min_val: int = 1, max_val: int = 100):
|
|
5
|
+
"""Fetches true random integers from random.org using their plain text API."""
|
|
6
|
+
url = "https://www.random.org/integers/"
|
|
7
|
+
params = {
|
|
8
|
+
"num": num,
|
|
9
|
+
"min": min_val,
|
|
10
|
+
"max": max_val,
|
|
11
|
+
"col": 1,
|
|
12
|
+
"base": 10,
|
|
13
|
+
"format": "plain",
|
|
14
|
+
"rnd": "new"
|
|
15
|
+
}
|
|
16
|
+
async with aiohttp.ClientSession() as session:
|
|
17
|
+
async with session.get(url, params=params) as response:
|
|
18
|
+
if response.status == 200:
|
|
19
|
+
text = await response.text()
|
|
20
|
+
return [int(x) for x in text.splitlines() if x.strip()]
|
|
21
|
+
return []
|
|
22
|
+
|
|
23
|
+
async def fetch_qrng_anu_integers(num: int = 1):
|
|
24
|
+
"""Fetches quantum random numbers from the Australian National University QRNG API."""
|
|
25
|
+
url = "https://qrng.anu.edu.au/API/jsonI.php"
|
|
26
|
+
params = {"length": num, "type": "uint8"}
|
|
27
|
+
async with aiohttp.ClientSession() as session:
|
|
28
|
+
async with session.get(url, params=params) as response:
|
|
29
|
+
if response.status == 200:
|
|
30
|
+
data = await response.json()
|
|
31
|
+
return data.get("data", [])
|
|
32
|
+
return []
|
|
33
|
+
|
|
34
|
+
async def fetch_roll_api_result():
|
|
35
|
+
"""Fetches a random dice roll from TheLastGimbus Roll-API (Physical Dice)."""
|
|
36
|
+
# Base URL found in research
|
|
37
|
+
base_url = "https://roll.lastgimbus.com/api"
|
|
38
|
+
|
|
39
|
+
async with aiohttp.ClientSession() as session:
|
|
40
|
+
try:
|
|
41
|
+
# Attempt to get a simple result first
|
|
42
|
+
# Research mentioned 'getRandomNumber' logic which suggests a GET might trigger it
|
|
43
|
+
# or we need to check a status.
|
|
44
|
+
# Trying the most likely 'roll' endpoint or root.
|
|
45
|
+
async with session.get(f"{base_url}/roll") as response:
|
|
46
|
+
if response.status == 200:
|
|
47
|
+
data = await response.json()
|
|
48
|
+
# Expecting 'result' or 'number'
|
|
49
|
+
return data.get("result", data.get("number", None))
|
|
50
|
+
|
|
51
|
+
# If 202 (Accepted/Queued), we might need to wait.
|
|
52
|
+
# Capturing status for debugging
|
|
53
|
+
if response.status != 404:
|
|
54
|
+
print(f"Roll-API Status: {response.status}")
|
|
55
|
+
|
|
56
|
+
# Fallback check on root if /roll fails
|
|
57
|
+
async with session.get(base_url) as response:
|
|
58
|
+
if response.status == 200:
|
|
59
|
+
text = await response.text()
|
|
60
|
+
# If it's just a number string
|
|
61
|
+
if text.isdigit():
|
|
62
|
+
return int(text)
|
|
63
|
+
# If json
|
|
64
|
+
try:
|
|
65
|
+
data = await response.json()
|
|
66
|
+
return data.get("val", data.get("result"))
|
|
67
|
+
except:
|
|
68
|
+
pass
|
|
69
|
+
|
|
70
|
+
return None
|
|
71
|
+
|
|
72
|
+
except Exception as e:
|
|
73
|
+
print(f"Roll-API Exception: {e}")
|
|
74
|
+
return None
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import pyautogui
|
|
2
|
+
import time
|
|
3
|
+
import ctypes
|
|
4
|
+
import random
|
|
5
|
+
|
|
6
|
+
# Windows constants for mouse buttons
|
|
7
|
+
VK_LBUTTON = 0x01
|
|
8
|
+
VK_RBUTTON = 0x02
|
|
9
|
+
VK_MBUTTON = 0x04
|
|
10
|
+
|
|
11
|
+
def is_left_click_pressed():
|
|
12
|
+
"""Checks if the left mouse button is currently down using Windows API."""
|
|
13
|
+
return ctypes.windll.user32.GetAsyncKeyState(VK_LBUTTON) & 0x8000
|
|
14
|
+
|
|
15
|
+
def is_right_click_pressed():
|
|
16
|
+
"""Checks if the right mouse button is currently down using Windows API."""
|
|
17
|
+
return ctypes.windll.user32.GetAsyncKeyState(VK_RBUTTON) & 0x8000
|
|
18
|
+
|
|
19
|
+
def is_middle_click_pressed():
|
|
20
|
+
"""Checks if the middle mouse button is currently down using Windows API."""
|
|
21
|
+
return ctypes.windll.user32.GetAsyncKeyState(VK_MBUTTON) & 0x8000
|
|
22
|
+
|
|
23
|
+
def get_click_data(button_check_func=is_left_click_pressed):
|
|
24
|
+
"""Waits for a specific click and returns (timestamp, cursor_position)."""
|
|
25
|
+
# Wait for the button to be pressed
|
|
26
|
+
while not button_check_func():
|
|
27
|
+
time.sleep(0.005) # Tiny sleep to prevent high CPU usage
|
|
28
|
+
|
|
29
|
+
t = time.perf_counter()
|
|
30
|
+
pos = pyautogui.position()
|
|
31
|
+
|
|
32
|
+
# Wait for the button to be released to avoid duplicate captures
|
|
33
|
+
while button_check_func():
|
|
34
|
+
time.sleep(0.005)
|
|
35
|
+
|
|
36
|
+
return t, pos
|
|
37
|
+
|
|
38
|
+
class RealRandomUsingBetweenLeftClicks(random.Random):
|
|
39
|
+
def seed(self, a=None, version=2):
|
|
40
|
+
if a is None:
|
|
41
|
+
t1, p1 = get_click_data(is_left_click_pressed)
|
|
42
|
+
t2, p2 = get_click_data(is_left_click_pressed)
|
|
43
|
+
# Use the time interval and positions to create a seed without hashing
|
|
44
|
+
a = int((t2 - t1) * 10**9) + p1.x + p1.y + p2.x + p2.y
|
|
45
|
+
super().seed(a, version=version)
|
|
46
|
+
|
|
47
|
+
class RealRandomUsingBetweenRightClicks(random.Random):
|
|
48
|
+
def seed(self, a=None, version=2):
|
|
49
|
+
if a is None:
|
|
50
|
+
t1, p1 = get_click_data(is_right_click_pressed)
|
|
51
|
+
t2, p2 = get_click_data(is_right_click_pressed)
|
|
52
|
+
a = int((t2 - t1) * 10**9) + p1.x + p1.y + p2.x + p2.y
|
|
53
|
+
super().seed(a, version=version)
|
|
54
|
+
|
|
55
|
+
class RealRandomUsingBetweenMiddleClicks(random.Random):
|
|
56
|
+
def seed(self, a=None, version=2):
|
|
57
|
+
if a is None:
|
|
58
|
+
t1, p1 = get_click_data(is_middle_click_pressed)
|
|
59
|
+
t2, p2 = get_click_data(is_middle_click_pressed)
|
|
60
|
+
a = int((t2 - t1) * 10**9) + p1.x + p1.y + p2.x + p2.y
|
|
61
|
+
super().seed(a, version=version)
|
|
62
|
+
|
|
63
|
+
class RealRandomUsingBetweenMixedClicks(random.Random):
|
|
64
|
+
def seed(self, a=None, version=2):
|
|
65
|
+
if a is None:
|
|
66
|
+
# Mixed logic: Left -> Right -> Middle
|
|
67
|
+
t1, p1 = get_click_data(is_left_click_pressed)
|
|
68
|
+
t2, p2 = get_click_data(is_right_click_pressed)
|
|
69
|
+
t3, p3 = get_click_data(is_middle_click_pressed)
|
|
70
|
+
a = int((t3 - t1) * 10**9) + p1.x + p1.y + p2.x + p2.y + p3.x + p3.y
|
|
71
|
+
super().seed(a, version=version)
|
|
72
|
+
|
|
73
|
+
def main():
|
|
74
|
+
print("--- Click-based Randomness Test ---")
|
|
75
|
+
print("Each generator requires specific mouse clicks to seed.")
|
|
76
|
+
|
|
77
|
+
print("\n1. Seeding Left-Click Random (requires 2 left clicks)...")
|
|
78
|
+
rl = RealRandomUsingBetweenLeftClicks()
|
|
79
|
+
print(f"Result: {rl.random()}")
|
|
80
|
+
|
|
81
|
+
print("\n2. Seeding Right-Click Random (requires 2 right clicks)...")
|
|
82
|
+
rr = RealRandomUsingBetweenRightClicks()
|
|
83
|
+
print(f"Result: {rr.random()}")
|
|
84
|
+
|
|
85
|
+
print("\n3. Seeding Mixed-Click Random (requires Left -> Right -> Middle)...")
|
|
86
|
+
rm = RealRandomUsingBetweenMixedClicks()
|
|
87
|
+
print(f"Result: {rm.random()}")
|
|
88
|
+
|
|
89
|
+
if __name__ == "__main__":
|
|
90
|
+
try:
|
|
91
|
+
main()
|
|
92
|
+
except KeyboardInterrupt:
|
|
93
|
+
print("\nStopped by user.")
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
print("coming soon :/")
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
# Safety Warnings
|
|
5
|
+
warnings.warn("WARNING: LCGs are NOT cryptographically secure. They use linear algebra and can be reversed.")
|
|
6
|
+
warnings.warn("This is only safe for OFFLINE GAMES with NO LEADERBOARDS.")
|
|
7
|
+
warnings.warn("See: https://youtu.be/XDsYPXRCXAs?si=oDaFsqZyNJWVXwEi")
|
|
8
|
+
|
|
9
|
+
class LCGs:
|
|
10
|
+
"""
|
|
11
|
+
Linear Congruential Generator (LCG) Implementation.
|
|
12
|
+
|
|
13
|
+
Summary:
|
|
14
|
+
This class implements a Linear Congruential Generator, a simple algorithm that yields a
|
|
15
|
+
sequence of pseudo-randomized numbers calculated with a linear equation.
|
|
16
|
+
|
|
17
|
+
WARNING:
|
|
18
|
+
LCGs are NOT safe for cryptography or high-stakes randomness (like gambling or leaderboards).
|
|
19
|
+
Because they are based on simple math (modular arithmetic), their state can be recovered
|
|
20
|
+
(reversed) if a few outputs are known, e.g., using Modular Multiplicative Inverse (MMI).
|
|
21
|
+
|
|
22
|
+
Required args:
|
|
23
|
+
rules: str - The forward formula, e.g., "(s * a + c) % d"
|
|
24
|
+
|
|
25
|
+
Optional args:
|
|
26
|
+
reverse_rules: str or None - The formula to reverse the state, proving insecurity.
|
|
27
|
+
e.g., "((s - c) * pow(a, -1, d)) % d"
|
|
28
|
+
s: int - Seed/State
|
|
29
|
+
a: int - Multiplier
|
|
30
|
+
c: int - Increment
|
|
31
|
+
d: int - Modulus
|
|
32
|
+
"""
|
|
33
|
+
def __init__(
|
|
34
|
+
self,
|
|
35
|
+
rules: str,
|
|
36
|
+
reverse_rules: str or None = None,
|
|
37
|
+
s: int = random.randint(1, 9999),
|
|
38
|
+
a: int = 10,
|
|
39
|
+
b: int = 3, # kept for backward compatibility if needed
|
|
40
|
+
c: int = 5,
|
|
41
|
+
d: int = 25565
|
|
42
|
+
):
|
|
43
|
+
if rules is None:
|
|
44
|
+
raise ValueError("rules must be provided (e.g., '(s * a + c) % d')")
|
|
45
|
+
|
|
46
|
+
self.rules = rules
|
|
47
|
+
self.reverse_rules = reverse_rules
|
|
48
|
+
self.s = s
|
|
49
|
+
self.a = a
|
|
50
|
+
self.b = b
|
|
51
|
+
self.c = c
|
|
52
|
+
self.d = d
|
|
53
|
+
|
|
54
|
+
def roll(self):
|
|
55
|
+
"""Advance the generator state forward."""
|
|
56
|
+
# Standard eval with restricted scope
|
|
57
|
+
self.s = eval(self.rules, {"__builtins__": None}, {
|
|
58
|
+
's': self.s, 'a': self.a, 'b': self.b,
|
|
59
|
+
'c': self.c, 'd': self.d
|
|
60
|
+
})
|
|
61
|
+
return self.s
|
|
62
|
+
|
|
63
|
+
def reverse_roll(self):
|
|
64
|
+
"""
|
|
65
|
+
Reverse the generator state backward.
|
|
66
|
+
|
|
67
|
+
This method demonstrates the insecurity of LCGs. If you know the parameters (a, c, d)
|
|
68
|
+
and the current state (s), you can calculate the PREVIOUS state using:
|
|
69
|
+
s_prev = (s - c) * a^(-1) mod d
|
|
70
|
+
"""
|
|
71
|
+
if self.reverse_rules is None:
|
|
72
|
+
raise ValueError("reverse_rules is not defined! Cannot reverse state.")
|
|
73
|
+
|
|
74
|
+
# Using pow(a, -1, d) for modular multiplicative inverse in python 3.8+
|
|
75
|
+
self.s = eval(self.reverse_rules, {"__builtins__": None, "pow": pow}, {
|
|
76
|
+
's': self.s, 'a': self.a, 'b': self.b,
|
|
77
|
+
'c': self.c, 'd': self.d
|
|
78
|
+
})
|
|
79
|
+
return self.s
|
|
80
|
+
|
|
81
|
+
def random(self, s: int or None = None):
|
|
82
|
+
"""Predict next bit/value based on roll (0 or 1)."""
|
|
83
|
+
if s is not None:
|
|
84
|
+
self.s = s
|
|
85
|
+
old_s = self.s
|
|
86
|
+
self.roll()
|
|
87
|
+
return 1 if old_s <= self.s else 0
|
|
88
|
+
|
|
89
|
+
def randint(self):
|
|
90
|
+
"""Returns the current raw state integer."""
|
|
91
|
+
self.roll()
|
|
92
|
+
return self.s
|
|
93
|
+
|
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
import random
|
|
2
|
+
import warnings
|
|
3
|
+
|
|
4
|
+
# Safety Warnings
|
|
5
|
+
warnings.warn("WARNING: XorShift/Xoshiro generators are SAFER than LCGs but still INSECURE for cryptography. They are pseudo-random and not true randomness.")
|
|
6
|
+
warnings.warn("It uses a linear feedback shift register (LFSR) to generate pseudo-random numbers.")
|
|
7
|
+
warnings.warn("See: https://youtu.be/XDsYPXRCXAs?si=oDaFsqZyNJWVXwEi")
|
|
8
|
+
|
|
9
|
+
def reverse_xor_lshift(x, s, bits=32):
|
|
10
|
+
"""Reverses x ^= x << s."""
|
|
11
|
+
res = x
|
|
12
|
+
mask = (1 << bits) - 1
|
|
13
|
+
for _ in range(bits // s):
|
|
14
|
+
res = x ^ ((res << s) & mask)
|
|
15
|
+
return res & mask
|
|
16
|
+
|
|
17
|
+
def reverse_xor_rshift(x, s, bits=32):
|
|
18
|
+
"""Reverses x ^= x >> s."""
|
|
19
|
+
res = x
|
|
20
|
+
mask = (1 << bits) - 1
|
|
21
|
+
for _ in range(bits // s):
|
|
22
|
+
res = x ^ (res >> s)
|
|
23
|
+
return res & mask
|
|
24
|
+
|
|
25
|
+
class Xorshift32:
|
|
26
|
+
"""
|
|
27
|
+
Marsaglia's original 32-bit Xorshift.
|
|
28
|
+
Passes some tests but too linear for modern standards.
|
|
29
|
+
"""
|
|
30
|
+
def __init__(self, seed: int = None, a: int = 13, b: int = 17, c: int = 5):
|
|
31
|
+
if seed is None:
|
|
32
|
+
seed = random.randint(1, 0xFFFFFFFF)
|
|
33
|
+
self.state = seed & 0xFFFFFFFF
|
|
34
|
+
if self.state == 0: self.state = 1
|
|
35
|
+
self.a = a
|
|
36
|
+
self.b = b
|
|
37
|
+
self.c = c
|
|
38
|
+
|
|
39
|
+
def roll(self):
|
|
40
|
+
x = self.state
|
|
41
|
+
x ^= (x << self.a) & 0xFFFFFFFF
|
|
42
|
+
x ^= (x >> self.b) & 0xFFFFFFFF
|
|
43
|
+
x ^= (x << self.c) & 0xFFFFFFFF
|
|
44
|
+
self.state = x
|
|
45
|
+
return self.state
|
|
46
|
+
|
|
47
|
+
def reverse_roll(self):
|
|
48
|
+
"""Demonstrates the reversibility of pure Xorshift."""
|
|
49
|
+
x = self.state
|
|
50
|
+
# Reverse x ^= x << c
|
|
51
|
+
x = reverse_xor_lshift(x, self.c, 32)
|
|
52
|
+
# Reverse x ^= x >> b
|
|
53
|
+
x = reverse_xor_rshift(x, self.b, 32)
|
|
54
|
+
# Reverse x ^= x << a
|
|
55
|
+
x = reverse_xor_lshift(x, self.a, 32)
|
|
56
|
+
self.state = x
|
|
57
|
+
return self.state
|
|
58
|
+
|
|
59
|
+
def random(self):
|
|
60
|
+
# Normalize to [0, 1)
|
|
61
|
+
return self.roll() / 0x100000000
|
|
62
|
+
|
|
63
|
+
class Xorshift128:
|
|
64
|
+
"""
|
|
65
|
+
Marsaglia's original 128-bit Xorshift.
|
|
66
|
+
Uses a 4-word state.
|
|
67
|
+
"""
|
|
68
|
+
def __init__(self, seeds: list = None, a: int = 11, b: int = 8, c: int = 19):
|
|
69
|
+
if seeds is None or len(seeds) < 4:
|
|
70
|
+
seeds = [random.randint(1, 0xFFFFFFFF) for _ in range(4)]
|
|
71
|
+
self.state = [s & 0xFFFFFFFF for s in seeds]
|
|
72
|
+
if all(s == 0 for s in self.state): self.state[0] = 1
|
|
73
|
+
self.a = a
|
|
74
|
+
self.b = b
|
|
75
|
+
self.c = c
|
|
76
|
+
|
|
77
|
+
def roll(self):
|
|
78
|
+
t = self.state[3]
|
|
79
|
+
s = self.state[0]
|
|
80
|
+
self.state[3] = self.state[2]
|
|
81
|
+
self.state[2] = self.state[1]
|
|
82
|
+
self.state[1] = s
|
|
83
|
+
|
|
84
|
+
t ^= (t << self.a) & 0xFFFFFFFF
|
|
85
|
+
t ^= (t >> self.b) & 0xFFFFFFFF
|
|
86
|
+
self.state[0] = (t ^ s ^ (s >> self.c)) & 0xFFFFFFFF
|
|
87
|
+
return self.state[0]
|
|
88
|
+
|
|
89
|
+
def reverse_roll(self):
|
|
90
|
+
"""Demonstrates reversibility of 128-bit version."""
|
|
91
|
+
s_prev = self.state[1]
|
|
92
|
+
res_xor_s = (self.state[0] ^ s_prev ^ (s_prev >> self.c)) & 0xFFFFFFFF
|
|
93
|
+
t_original = reverse_xor_rshift(reverse_xor_lshift(res_xor_s, self.a, 32), self.b, 32)
|
|
94
|
+
|
|
95
|
+
# Shift back
|
|
96
|
+
self.state[0] = self.state[1]
|
|
97
|
+
self.state[1] = self.state[2]
|
|
98
|
+
self.state[2] = self.state[3]
|
|
99
|
+
self.state[3] = t_original
|
|
100
|
+
return self.state[0]
|
|
101
|
+
|
|
102
|
+
class Xorwow:
|
|
103
|
+
"""Xorwow adds a Weyl sequence to fix linearity issues."""
|
|
104
|
+
def __init__(self, seeds: list = None):
|
|
105
|
+
if seeds is None or len(seeds) < 5:
|
|
106
|
+
seeds = [random.randint(1, 0xFFFFFFFF) for _ in range(5)]
|
|
107
|
+
self.state = [s & 0xFFFFFFFF for s in seeds]
|
|
108
|
+
self.counter = 0
|
|
109
|
+
|
|
110
|
+
def roll(self):
|
|
111
|
+
t = self.state[4]
|
|
112
|
+
s = self.state[0]
|
|
113
|
+
self.state[4] = self.state[3]
|
|
114
|
+
self.state[3] = self.state[2]
|
|
115
|
+
self.state[2] = self.state[1]
|
|
116
|
+
self.state[1] = s
|
|
117
|
+
|
|
118
|
+
t ^= (t >> 2) & 0xFFFFFFFF
|
|
119
|
+
t ^= (t << 1) & 0xFFFFFFFF
|
|
120
|
+
t ^= (s ^ (s << 4)) & 0xFFFFFFFF
|
|
121
|
+
self.state[0] = t
|
|
122
|
+
self.counter = (self.counter + 362437) & 0xFFFFFFFF
|
|
123
|
+
return (t + self.counter) & 0xFFFFFFFF
|
|
124
|
+
|
|
125
|
+
class XorshiftStar:
|
|
126
|
+
"""Xorshift* multiplies outputs by a constant."""
|
|
127
|
+
def __init__(self, seed: int = None, a: int = 12, b: int = 25, c: int = 27):
|
|
128
|
+
self.state = seed or random.randint(1, 0xFFFFFFFFFFFFFFFF)
|
|
129
|
+
self.a = a
|
|
130
|
+
self.b = b
|
|
131
|
+
self.c = c
|
|
132
|
+
|
|
133
|
+
def roll(self):
|
|
134
|
+
x = self.state
|
|
135
|
+
x ^= (x >> self.a) & 0xFFFFFFFFFFFFFFFF
|
|
136
|
+
x ^= (x << self.b) & 0xFFFFFFFFFFFFFFFF
|
|
137
|
+
x ^= (x >> self.c) & 0xFFFFFFFFFFFFFFFF
|
|
138
|
+
self.state = x
|
|
139
|
+
return (x * 0x2545F4914F6CDD1D) & 0xFFFFFFFFFFFFFFFF
|
|
140
|
+
|
|
141
|
+
class XorshiftPlus:
|
|
142
|
+
"""Xorshift128+ used in V8 and Safari."""
|
|
143
|
+
def __init__(self, s0: int = None, s1: int = None, a: int = 23, b: int = 17, c: int = 26):
|
|
144
|
+
self.s0 = s0 or random.randint(1, 0xFFFFFFFFFFFFFFFF)
|
|
145
|
+
self.s1 = s1 or random.randint(1, 0xFFFFFFFFFFFFFFFF)
|
|
146
|
+
self.a = a
|
|
147
|
+
self.b = b
|
|
148
|
+
self.c = c
|
|
149
|
+
|
|
150
|
+
def roll(self):
|
|
151
|
+
x = self.s0
|
|
152
|
+
y = self.s1
|
|
153
|
+
self.s0 = y
|
|
154
|
+
x ^= (x << self.a) & 0xFFFFFFFFFFFFFFFF
|
|
155
|
+
self.s1 = (x ^ y ^ (x >> self.b) ^ (y >> self.c)) & 0xFFFFFFFFFFFFFFFF
|
|
156
|
+
return (self.s1 + y) & 0xFFFFFFFFFFFFFFFF
|
|
157
|
+
|
|
158
|
+
def rotl(x, k, bits=64):
|
|
159
|
+
mask = (1 << bits) - 1
|
|
160
|
+
return ((x << k) & mask) | (x >> (bits - k))
|
|
161
|
+
|
|
162
|
+
class Xoshiro256StarStar:
|
|
163
|
+
"""Modern robust 64-bit generator."""
|
|
164
|
+
def __init__(self, seeds: list = None):
|
|
165
|
+
if seeds is None or len(seeds) < 4:
|
|
166
|
+
seeds = [random.randint(1, 0xFFFFFFFFFFFFFFFF) for _ in range(4)]
|
|
167
|
+
self.s = [s & 0xFFFFFFFFFFFFFFFF for s in seeds]
|
|
168
|
+
|
|
169
|
+
def roll(self):
|
|
170
|
+
res = (rotl((self.s[1] * 5) & 0xFFFFFFFFFFFFFFFF, 7) * 9) & 0xFFFFFFFFFFFFFFFF
|
|
171
|
+
t = (self.s[1] << 17) & 0xFFFFFFFFFFFFFFFF
|
|
172
|
+
|
|
173
|
+
self.s[2] ^= self.s[0]
|
|
174
|
+
self.s[3] ^= self.s[1]
|
|
175
|
+
self.s[1] ^= self.s[2]
|
|
176
|
+
self.s[0] ^= self.s[3]
|
|
177
|
+
self.s[2] ^= t
|
|
178
|
+
self.s[3] = rotl(self.s[3], 45)
|
|
179
|
+
|
|
180
|
+
return res
|
|
181
|
+
|
|
182
|
+
class Xoshiro128Plus:
|
|
183
|
+
"""32-bit optimized modern generator."""
|
|
184
|
+
def __init__(self, seeds: list = None):
|
|
185
|
+
if seeds is None or len(seeds) < 4:
|
|
186
|
+
seeds = [random.randint(1, 0xFFFFFFFF) for _ in range(4)]
|
|
187
|
+
self.s = [s & 0xFFFFFFFF for s in seeds]
|
|
188
|
+
|
|
189
|
+
def roll(self):
|
|
190
|
+
res = (self.s[0] + self.s[3]) & 0xFFFFFFFF
|
|
191
|
+
t = (self.s[1] << 9) & 0xFFFFFFFF
|
|
192
|
+
|
|
193
|
+
self.s[2] ^= self.s[0]
|
|
194
|
+
self.s[3] ^= self.s[1]
|
|
195
|
+
self.s[1] ^= self.s[2]
|
|
196
|
+
self.s[0] ^= self.s[3]
|
|
197
|
+
self.s[2] ^= t
|
|
198
|
+
self.s[3] = rotl(self.s[3], 11, bits=32)
|
|
199
|
+
|
|
200
|
+
return res
|
|
201
|
+
|
|
202
|
+
class Xoroshiro128PlusPlus:
|
|
203
|
+
"""
|
|
204
|
+
Xoroshiro128++ generator.
|
|
205
|
+
Used in modern Minecraft (Java 1.18+) for world generation and other random tasks.
|
|
206
|
+
"""
|
|
207
|
+
def __init__(self, seeds: list = None):
|
|
208
|
+
if seeds is None or len(seeds) < 2:
|
|
209
|
+
seeds = [random.randint(1, 0xFFFFFFFFFFFFFFFF) for _ in range(2)]
|
|
210
|
+
self.s = [s & 0xFFFFFFFFFFFFFFFF for s in seeds]
|
|
211
|
+
if all(s == 0 for s in self.s):
|
|
212
|
+
self.s[0] = 1
|
|
213
|
+
|
|
214
|
+
def roll(self):
|
|
215
|
+
s0 = self.s[0]
|
|
216
|
+
s1 = self.s[1]
|
|
217
|
+
res = (rotl((s0 + s1) & 0xFFFFFFFFFFFFFFFF, 17) + s0) & 0xFFFFFFFFFFFFFFFF
|
|
218
|
+
|
|
219
|
+
s1 ^= s0
|
|
220
|
+
self.s[0] = (rotl(s0, 49) ^ s1 ^ ((s1 << 21) & 0xFFFFFFFFFFFFFFFF)) & 0xFFFFFFFFFFFFFFFF
|
|
221
|
+
self.s[1] = rotl(s1, 28)
|
|
222
|
+
|
|
223
|
+
return res
|
|
224
|
+
|
|
225
|
+
|
|
226
|
+
if __name__ == "__main__":
|
|
227
|
+
print("Testing Xorshift32 Reverse...")
|
|
228
|
+
gen = Xorshift32(42)
|
|
229
|
+
original = gen.state
|
|
230
|
+
gen.roll()
|
|
231
|
+
print(f"Post Roll: {gen.state}")
|
|
232
|
+
gen.reverse_roll()
|
|
233
|
+
print(f"Post Reverse: {gen.state}")
|
|
234
|
+
assert gen.state == original
|
|
235
|
+
print("Success!")
|