Last active
June 6, 2026 02:02
-
-
Save aoirint/759a46aa283a40bf3bdf8b1153d8858a to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| """ | |
| # Mono System.Random Compatibility Implementation | |
| This implementation targets compatibility with the historical | |
| System.Random implementation used by Mono-based runtimes. | |
| ## Implementation Reference | |
| This implementation is a Python port of the .NET Framework 4.6.2 | |
| Reference Source implementation of System.Random. | |
| Reference: | |
| https://github.com/microsoft/referencesource/blob/4fe4349175f4c5091d972a7e56ea12012f1e7170/mscorlib/system/random.cs | |
| See the accompanying NOTICE file for attribution information. | |
| ## Goals | |
| The primary goal of this module is deterministic reproduction of the | |
| historical System.Random behavior used by Mono-based runtimes. | |
| Given the same seed and the same sequence of API calls, compatible | |
| implementations are expected to produce the same pseudorandom sequence. | |
| ## Intended Use Cases | |
| - Runtime behavior analysis | |
| - Cross-language tooling | |
| - Automated testing | |
| ## Non-Goals | |
| This implementation is not intended for cryptographic, | |
| security-sensitive, or adversarial environments. | |
| The underlying algorithm is designed for repeatability and statistical | |
| usefulness rather than cryptographic security. | |
| ## Algorithm | |
| This module implements a subtractive random number generator. | |
| The generator maintains an internal state consisting of: | |
| - A 56-element integer state array | |
| - Two moving state indices | |
| - A subtraction-based recurrence relation used to produce the next value | |
| Each generated value is derived from the difference between two previously | |
| stored state values. The resulting value is normalized into the valid integer | |
| range and written back into the state array, forming the basis for subsequent | |
| outputs. | |
| """ | |
| from __future__ import annotations | |
| from typing import Final, overload | |
| class MonoSystemRandom: | |
| """ | |
| A subtractive random number generator with a 56-element state array. | |
| The algorithm maintains two moving indices into the state array and | |
| generates each output from the difference of two previously stored values. | |
| Generated values are fed back into the state array, producing a | |
| deterministic pseudorandom sequence from an initial seed. | |
| """ | |
| _MBIG: Final[int] = 2_147_483_647 | |
| _MSEED: Final[int] = 161_803_398 | |
| _seed_array: list[int] | |
| _inext: int | |
| _inextp: int | |
| def __init__(self, seed: int) -> None: | |
| """ | |
| Initialize the generator state from an integer seed. | |
| """ | |
| self._seed_array = [0] * 56 | |
| subtraction = self._MBIG if seed == -2_147_483_648 else abs(seed) | |
| mj = self._MSEED - subtraction | |
| if mj < 0: | |
| mj += self._MBIG | |
| self._seed_array[55] = mj | |
| mk = 1 | |
| for i in range(1, 55): | |
| ii = (21 * i) % 55 | |
| self._seed_array[ii] = mk | |
| mk = mj - mk | |
| if mk < 0: | |
| mk += self._MBIG | |
| mj = self._seed_array[ii] | |
| for _ in range(4): | |
| for i in range(1, 56): | |
| self._seed_array[i] -= self._seed_array[ | |
| 1 + (i + 30) % 55 | |
| ] | |
| if self._seed_array[i] < 0: | |
| self._seed_array[i] += self._MBIG | |
| self._inext = 0 | |
| self._inextp = 21 | |
| def _internal_sample(self) -> int: | |
| """ | |
| Generate a raw integer sample. | |
| Returns: | |
| An integer satisfying: | |
| 0 <= value < 2_147_483_647 | |
| """ | |
| loc_inext = self._inext + 1 | |
| if loc_inext >= 56: | |
| loc_inext = 1 | |
| loc_inextp = self._inextp + 1 | |
| if loc_inextp >= 56: | |
| loc_inextp = 1 | |
| result = ( | |
| self._seed_array[loc_inext] | |
| - self._seed_array[loc_inextp] | |
| ) | |
| if result == self._MBIG: | |
| result -= 1 | |
| if result < 0: | |
| result += self._MBIG | |
| self._seed_array[loc_inext] = result | |
| self._inext = loc_inext | |
| self._inextp = loc_inextp | |
| return result | |
| def _sample(self) -> float: | |
| """ | |
| Generate a floating-point sample in the half-open range: | |
| 0.0 <= value < 1.0 | |
| """ | |
| return self._internal_sample() * (1.0 / self._MBIG) | |
| def _get_sample_for_large_range(self) -> float: | |
| """ | |
| Generate a floating-point sample suitable for ranges wider than MBIG. | |
| One sample determines magnitude and another determines sign, | |
| expanding the effective sampling range. | |
| """ | |
| result = self._internal_sample() | |
| if self._internal_sample() % 2 == 0: | |
| result = -result | |
| d = result + (self._MBIG - 1) | |
| return d / (2 * self._MBIG - 1) | |
| @overload | |
| def next(self) -> int: ... | |
| @overload | |
| def next(self, max_value: int) -> int: ... | |
| @overload | |
| def next(self, min_value: int, max_value: int) -> int: ... | |
| def next( | |
| self, | |
| min_value: int | None = None, | |
| max_value: int | None = None, | |
| ) -> int: | |
| """ | |
| Generate an integer sample. | |
| Supported call forms: | |
| next() | |
| next(max_value) | |
| next(min_value, max_value) | |
| For bounded calls, max_value is exclusive. | |
| """ | |
| if min_value is None and max_value is None: | |
| return self._internal_sample() | |
| if max_value is None: | |
| max_value = min_value | |
| min_value = 0 | |
| if max_value < 0: | |
| raise ValueError("max_value must be >= 0") | |
| if min_value > max_value: | |
| raise ValueError("min_value must be <= max_value") | |
| span = max_value - min_value | |
| if span <= self._MBIG: | |
| return int(self._sample() * span) + min_value | |
| return int(self._get_sample_for_large_range() * span) + min_value | |
| def next_double(self) -> float: | |
| """ | |
| Generate a floating-point sample in the half-open range: | |
| 0.0 <= value < 1.0 | |
| """ | |
| return self._sample() | |
| def next_bytes(self, length: int) -> bytes: | |
| """ | |
| Generate a bytes object of the requested length. | |
| Each output byte is derived from one raw integer sample. | |
| """ | |
| if length < 0: | |
| raise ValueError("length must be >= 0") | |
| return bytes( | |
| self._internal_sample() % 256 | |
| for _ in range(length) | |
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| MIT License | |
| Copyright (c) Microsoft Corporation | |
| Copyright (c) 2026 aoirint | |
| Permission is hereby granted, free of charge, to any person obtaining a copy | |
| of this software and associated documentation files (the "Software"), to deal | |
| in the Software without restriction, including without limitation the rights | |
| to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
| copies of the Software, and to permit persons to whom the Software is | |
| furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in all | |
| copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
| AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
| LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
| OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
| SOFTWARE. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| This project contains a Python port of the .NET Framework 4.6.2 | |
| Reference Source implementation of System.Random. | |
| Reference: | |
| https://github.com/microsoft/referencesource/blob/4fe4349175f4c5091d972a7e56ea12012f1e7170/mscorlib/system/random.cs | |
| Copyright (c) Microsoft Corporation | |
| Modifications and Python port: | |
| Copyright (c) 2026 aoirint |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment