3fe3634dc06a5339cfd939ba01062d4778f4f064
Marco Ricci Change the author e-mail ad...

Marco Ricci authored 2 months ago

src/derivepassphrase/sequin.py   1) # SPDX-FileCopyrightText: 2024 Marco Ricci <software@the13thletter.info>
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py           2) #
src/sequin/__init__.py           3) # SPDX-License-Identifier: MIT
src/sequin/__init__.py           4) 
src/sequin/__init__.py           5) """A Python reimplementation of James Coglan's "sequin" Node.js module.
src/sequin/__init__.py           6) 
src/sequin/__init__.py           7) James Coglan's "sequin" Node.js module provides a pseudorandom number
src/sequin/__init__.py           8) generator (using rejection sampling on a stream of input numbers) that
src/sequin/__init__.py           9) attempts to minimize the amount of information it throws away:
src/sequin/__init__.py          10) (non-degenerate) rejected samples are fed into a stream of higher-order
src/sequin/__init__.py          11) numbers from which the next random number generation request will be
src/sequin/__init__.py          12) served.  The sequin module is used in Coglan's "vault" module (a
src/sequin/__init__.py          13) deterministic, stateless password manager that recomputes passwords
src/sequin/__init__.py          14) instead of storing them), and this reimplementation is used for
src/sequin/__init__.py          15) a similar purpose.
src/sequin/__init__.py          16) 
Marco Ricci Generate nicer documentatio...

Marco Ricci authored 1 month ago

src/derivepassphrase/sequin.py  17) The main API is the [`Sequin`][] class, which is thoroughly documented.
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py          18) 
src/sequin/__init__.py          19) """
src/sequin/__init__.py          20) 
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py          21) # ruff: noqa: RUF002,RUF003
src/sequin/__init__.py          22) 
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py          23) from __future__ import annotations
src/sequin/__init__.py          24) 
src/sequin/__init__.py          25) import collections
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py          26) from typing import TYPE_CHECKING
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py          27) 
Marco Ricci Support Python 3.10 and PyP...

Marco Ricci authored 3 months ago

src/sequin/__init__.py          28) from typing_extensions import assert_type
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py          29) 
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py          30) if TYPE_CHECKING:
src/sequin/__init__.py          31)     from collections.abc import Iterator, Sequence
src/sequin/__init__.py          32) 
Marco Ricci Rename SequinExhaustedExcep...

Marco Ricci authored 4 months ago

src/sequin/__init__.py          33) __all__ = ('Sequin', 'SequinExhaustedError')
Marco Ricci Change the author e-mail ad...

Marco Ricci authored 2 months ago

src/derivepassphrase/sequin.py  34) __author__ = 'Marco Ricci <software@the13thletter.info>'
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py          35) 
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py          36) 
src/sequin/__init__.py          37) class Sequin:
src/sequin/__init__.py          38)     """Generate pseudorandom non-negative numbers in different ranges.
src/sequin/__init__.py          39) 
src/sequin/__init__.py          40)     Given a (presumed high-quality) uniformly random sequence of input
src/sequin/__init__.py          41)     bits, generate pseudorandom non-negative integers in a certain range
src/sequin/__init__.py          42)     on each call of the `generate` method.  (It is permissible to
src/sequin/__init__.py          43)     specify a different range per call to `generate`; this is the main
src/sequin/__init__.py          44)     use case.)  We use a modified version of rejection sampling, where
src/sequin/__init__.py          45)     rejected values are stored in "rejection queues" if possible, and
src/sequin/__init__.py          46)     these rejection queues re-seed the next round of rejection sampling.
src/sequin/__init__.py          47) 
src/sequin/__init__.py          48)     This is a Python reimplementation of James Coglan's [Node.js sequin
src/sequin/__init__.py          49)     module][JS_SEQUIN], as introduced in [his blog post][BLOG_POST].  It
src/sequin/__init__.py          50)     uses a [technique by Christian Lawson-Perfect][SEQUIN_TECHNIQUE].
src/sequin/__init__.py          51)     I do not know why the original module is called "sequin"; I presume
src/sequin/__init__.py          52)     it to be a pun on "sequence".
src/sequin/__init__.py          53) 
src/sequin/__init__.py          54)     [JS_SEQUIN]: https://www.npmjs.com/package/sequin
src/sequin/__init__.py          55)     [BLOG_POST]: https://blog.jcoglan.com/2012/07/16/designing-vaults-generator-algorithm/
src/sequin/__init__.py          56)     [SEQUIN_TECHNIQUE]: https://checkmyworking.com/2012/06/converting-a-stream-of-binary-digits-to-a-stream-of-base-n-digits/
src/sequin/__init__.py          57) 
src/sequin/__init__.py          58)     """
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py          59) 
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py          60)     def __init__(
src/sequin/__init__.py          61)         self,
src/sequin/__init__.py          62)         sequence: str | bytes | bytearray | Sequence[int],
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py          63)         /,
src/sequin/__init__.py          64)         *,
src/sequin/__init__.py          65)         is_bitstring: bool = False,
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 2 months ago

src/derivepassphrase/sequin.py  66)     ) -> None:
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py          67)         """Initialize the Sequin.
src/sequin/__init__.py          68) 
src/sequin/__init__.py          69)         Args:
src/sequin/__init__.py          70)             sequence:
src/sequin/__init__.py          71)                 A sequence of bits, or things convertible to bits, to
src/sequin/__init__.py          72)                 seed the pseudorandom number generator.  Byte and text
src/sequin/__init__.py          73)                 strings are converted to 8-bit integer sequences.
src/sequin/__init__.py          74)                 (Conversion will fail if the text string contains
src/sequin/__init__.py          75)                 non-ISO-8859-1 characters.)  The numbers are then
src/sequin/__init__.py          76)                 converted to bits.
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py          77)             is_bitstring:
src/sequin/__init__.py          78)                 If true, treat the input as a bitstring.  By default,
src/sequin/__init__.py          79)                 the input is treated as a string of 8-bit integers, from
src/sequin/__init__.py          80)                 which the individual bits must still be extracted.
src/sequin/__init__.py          81) 
src/sequin/__init__.py          82)         Raises:
src/sequin/__init__.py          83)             ValueError:
src/sequin/__init__.py          84)                 The sequence contains values outside the permissible
src/sequin/__init__.py          85)                 range.
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py          86) 
src/sequin/__init__.py          87)         """
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py          88)         msg = 'sequence item out of range'
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py          89) 
Marco Ricci Fix typing issues in mypy s...

Marco Ricci authored 3 months ago

src/sequin/__init__.py          90)         def uint8_to_bits(value: int) -> Iterator[int]:
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py          91)             """Yield individual bits of an 8-bit number, MSB first."""
src/sequin/__init__.py          92)             for i in (0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01):
src/sequin/__init__.py          93)                 yield 1 if value | i == value else 0
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py          94) 
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py          95)         if isinstance(sequence, str):
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py          96)             try:
src/sequin/__init__.py          97)                 sequence = tuple(sequence.encode('iso-8859-1'))
src/sequin/__init__.py          98)             except UnicodeError as e:
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py          99)                 raise ValueError(msg) from e
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         100)         else:
src/sequin/__init__.py         101)             sequence = tuple(sequence)
src/sequin/__init__.py         102)         assert_type(sequence, tuple[int, ...])
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         103)         self.bases: dict[int, collections.deque[int]] = {}
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         104) 
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         105)         def gen() -> Iterator[int]:
src/sequin/__init__.py         106)             for num in sequence:
src/sequin/__init__.py         107)                 if num not in range(2 if is_bitstring else 256):
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py         108)                     raise ValueError(msg)
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         109)                 if is_bitstring:
src/sequin/__init__.py         110)                     yield num
src/sequin/__init__.py         111)                 else:
src/sequin/__init__.py         112)                     yield from uint8_to_bits(num)
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         113) 
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         114)         self.bases[2] = collections.deque(gen())
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         115) 
src/sequin/__init__.py         116)     def _all_or_nothing_shift(
src/sequin/__init__.py         117)         self, count: int, /, *, base: int = 2
src/sequin/__init__.py         118)     ) -> Sequence[int]:
src/sequin/__init__.py         119)         """Shift and return items if and only if there are enough.
src/sequin/__init__.py         120) 
src/sequin/__init__.py         121)         Args:
src/sequin/__init__.py         122)             count: Number of items to shift/consume.
src/sequin/__init__.py         123)             base: Use the base `base` sequence.
src/sequin/__init__.py         124) 
src/sequin/__init__.py         125)         Returns:
src/sequin/__init__.py         126)             If there are sufficient items in the sequence left, then
src/sequin/__init__.py         127)             consume them from the sequence and return them.  Otherwise,
src/sequin/__init__.py         128)             consume nothing, and return nothing.
src/sequin/__init__.py         129) 
src/sequin/__init__.py         130)         Notes:
src/sequin/__init__.py         131)             We currently remove now-empty sequences from the registry of
src/sequin/__init__.py         132)             sequences.
src/sequin/__init__.py         133) 
src/sequin/__init__.py         134)         Examples:
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         135)             >>> seq = Sequin([1, 0, 1, 0, 0, 1, 0, 0, 0, 1], is_bitstring=True)
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         136)             >>> seq.bases
src/sequin/__init__.py         137)             {2: deque([1, 0, 1, 0, 0, 1, 0, 0, 0, 1])}
src/sequin/__init__.py         138)             >>> seq._all_or_nothing_shift(3)
src/sequin/__init__.py         139)             (1, 0, 1)
src/sequin/__init__.py         140)             >>> seq._all_or_nothing_shift(3)
src/sequin/__init__.py         141)             (0, 0, 1)
src/sequin/__init__.py         142)             >>> seq.bases[2]
src/sequin/__init__.py         143)             deque([0, 0, 0, 1])
src/sequin/__init__.py         144)             >>> seq._all_or_nothing_shift(5)
src/sequin/__init__.py         145)             ()
src/sequin/__init__.py         146)             >>> seq.bases[2]
src/sequin/__init__.py         147)             deque([0, 0, 0, 1])
src/sequin/__init__.py         148)             >>> seq._all_or_nothing_shift(4)
src/sequin/__init__.py         149)             (0, 0, 0, 1)
src/sequin/__init__.py         150)             >>> 2 in seq.bases  # now-empty sequences are removed
src/sequin/__init__.py         151)             False
src/sequin/__init__.py         152) 
src/sequin/__init__.py         153)         """
src/sequin/__init__.py         154)         try:
src/sequin/__init__.py         155)             seq = self.bases[base]
src/sequin/__init__.py         156)         except KeyError:
src/sequin/__init__.py         157)             return ()
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         158)         stash: collections.deque[int] = collections.deque()
src/sequin/__init__.py         159)         try:
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py         160)             for _ in range(count):
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         161)                 stash.append(seq.popleft())
src/sequin/__init__.py         162)         except IndexError:
src/sequin/__init__.py         163)             seq.extendleft(reversed(stash))
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         164)             return ()
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         165)         # Clean up queues.
src/sequin/__init__.py         166)         if not seq:
src/sequin/__init__.py         167)             del self.bases[base]
src/sequin/__init__.py         168)         return tuple(stash)
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         169) 
src/sequin/__init__.py         170)     @staticmethod
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         171)     def _big_endian_number(digits: Sequence[int], /, *, base: int = 2) -> int:
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         172)         """Evaluate the given integer sequence as a big endian number.
src/sequin/__init__.py         173) 
src/sequin/__init__.py         174)         Args:
src/sequin/__init__.py         175)             digits: A sequence of integers to evaluate.
src/sequin/__init__.py         176)             base: The number base to evaluate those integers in.
src/sequin/__init__.py         177) 
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 2 months ago

src/derivepassphrase/sequin.py 178)         Returns:
src/derivepassphrase/sequin.py 179)             The number value of the integer sequence.
src/derivepassphrase/sequin.py 180) 
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         181)         Raises:
src/sequin/__init__.py         182)             ValueError: `base` is an invalid base.
src/sequin/__init__.py         183)             ValueError: Not all integers are valid base `base` digits.
src/sequin/__init__.py         184) 
src/sequin/__init__.py         185)         Examples:
src/sequin/__init__.py         186)             >>> Sequin._big_endian_number([1, 2, 3, 4, 5, 6, 7, 8], base=10)
src/sequin/__init__.py         187)             12345678
Marco Ricci Add unit tests, both new an...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         188)             >>> Sequin._big_endian_number([1, 2, 3, 4, 5, 6, 7, 8], base=100)
src/sequin/__init__.py         189)             102030405060708
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         190)             >>> Sequin._big_endian_number([0, 0, 0, 0, 1, 4, 9, 7], base=10)
src/sequin/__init__.py         191)             1497
src/sequin/__init__.py         192)             >>> Sequin._big_endian_number([1, 0, 0, 1, 0, 0, 0, 0], base=2)
src/sequin/__init__.py         193)             144
src/sequin/__init__.py         194)             >>> Sequin._big_endian_number([1, 7, 5, 5], base=8) == 0o1755
src/sequin/__init__.py         195)             True
src/sequin/__init__.py         196) 
src/sequin/__init__.py         197)         """
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py         198)         if base < 2:  # noqa: PLR2004
src/sequin/__init__.py         199)             msg = f'invalid base: {base!r}'
src/sequin/__init__.py         200)             raise ValueError(msg)
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         201)         ret = 0
src/sequin/__init__.py         202)         allowed_range = range(base)
src/sequin/__init__.py         203)         n = len(digits)
src/sequin/__init__.py         204)         for i in range(n):
src/sequin/__init__.py         205)             i2 = (n - 1) - i
src/sequin/__init__.py         206)             x = digits[i]
src/sequin/__init__.py         207)             if not isinstance(x, int):
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py         208)                 msg = f'not an integer: {x!r}'
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 2 months ago

src/derivepassphrase/sequin.py 209)                 raise TypeError(msg)  # noqa: DOC501
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         210)             if x not in allowed_range:
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py         211)                 msg = f'invalid base {base!r} digit: {x!r}'
src/sequin/__init__.py         212)                 raise ValueError(msg)
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         213)             ret += (base**i2) * x
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         214)         return ret
src/sequin/__init__.py         215) 
src/sequin/__init__.py         216)     def generate(self, n: int, /) -> int:
src/sequin/__init__.py         217)         """Generate a base `n` non-negative integer.
src/sequin/__init__.py         218) 
src/sequin/__init__.py         219)         We attempt to generate a value using rejection sampling.  If the
src/sequin/__init__.py         220)         generated sample is outside the desired range (i.e., is
src/sequin/__init__.py         221)         rejected), then attempt to reuse the sample by seeding
src/sequin/__init__.py         222)         a "higher-order" input sequence of uniformly random numbers (for
src/sequin/__init__.py         223)         a different base).
src/sequin/__init__.py         224) 
src/sequin/__init__.py         225)         Args:
src/sequin/__init__.py         226)             n:
src/sequin/__init__.py         227)                 Generate numbers in the range 0, ..., `n` - 1.
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         228)                 (Inclusive.)  Must be larger than 0.
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         229) 
src/sequin/__init__.py         230)         Returns:
src/sequin/__init__.py         231)             A pseudorandom number in the range 0, ..., `n` - 1.
src/sequin/__init__.py         232) 
src/sequin/__init__.py         233)         Raises:
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         234)             ValueError:
src/sequin/__init__.py         235)                 The range is empty.
Marco Ricci Rename SequinExhaustedExcep...

Marco Ricci authored 4 months ago

src/sequin/__init__.py         236)             SequinExhaustedError:
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         237)                 The sequin is exhausted.
src/sequin/__init__.py         238) 
Marco Ricci Add unit tests, both new an...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         239)         Examples:
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         240)             >>> seq = Sequin(
src/sequin/__init__.py         241)             ...     [1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1],
src/sequin/__init__.py         242)             ...     is_bitstring=True,
src/sequin/__init__.py         243)             ... )
src/sequin/__init__.py         244)             >>> seq2 = Sequin(
src/sequin/__init__.py         245)             ...     [1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1],
src/sequin/__init__.py         246)             ...     is_bitstring=True,
src/sequin/__init__.py         247)             ... )
Marco Ricci Add unit tests, both new an...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         248)             >>> seq.generate(5)
src/sequin/__init__.py         249)             3
src/sequin/__init__.py         250)             >>> seq.generate(5)
src/sequin/__init__.py         251)             3
src/sequin/__init__.py         252)             >>> seq.generate(5)
src/sequin/__init__.py         253)             1
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         254)             >>> seq.generate(5)  # doctest: +IGNORE_EXCEPTION_DETAIL
Marco Ricci Add unit tests, both new an...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         255)             Traceback (most recent call last):
src/sequin/__init__.py         256)                 ...
Marco Ricci Rename SequinExhaustedExcep...

Marco Ricci authored 4 months ago

src/sequin/__init__.py         257)             SequinExhaustedError: Sequin is exhausted
Marco Ricci Add unit tests, both new an...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         258) 
src/sequin/__init__.py         259)             Using `n = 1` does not actually consume input bits:
src/sequin/__init__.py         260) 
src/sequin/__init__.py         261)             >>> seq2.generate(1)
src/sequin/__init__.py         262)             0
src/sequin/__init__.py         263) 
src/sequin/__init__.py         264)             But it still won't work on exhausted sequins:
src/sequin/__init__.py         265) 
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         266)             >>> seq.generate(1)  # doctest: +IGNORE_EXCEPTION_DETAIL
Marco Ricci Add unit tests, both new an...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         267)             Traceback (most recent call last):
src/sequin/__init__.py         268)                 ...
Marco Ricci Rename SequinExhaustedExcep...

Marco Ricci authored 4 months ago

src/sequin/__init__.py         269)             SequinExhaustedError: Sequin is exhausted
Marco Ricci Add unit tests, both new an...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         270) 
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         271)         """
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py         272)         if 2 not in self.bases:  # noqa: PLR2004
src/sequin/__init__.py         273)             raise SequinExhaustedError
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         274)         value = self._generate_inner(n, base=2)
src/sequin/__init__.py         275)         if value == n:
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py         276)             raise SequinExhaustedError
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         277)         return value
src/sequin/__init__.py         278) 
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         279)     def _generate_inner(self, n: int, /, *, base: int = 2) -> int:
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         280)         """Recursive call to generate a base `n` non-negative integer.
src/sequin/__init__.py         281) 
src/sequin/__init__.py         282)         We first determine the correct exponent `k` to generate base `n`
src/sequin/__init__.py         283)         numbers from a stream of base `base` numbers, then attempt to
src/sequin/__init__.py         284)         take `k` numbers from the base `base` sequence (or bail if not
src/sequin/__init__.py         285)         possible).  If the resulting number `v` is out of range for
src/sequin/__init__.py         286)         base `n`, then push `v - n` onto the rejection queue for
Marco Ricci Fix miscellaneous small doc...

Marco Ricci authored 2 months ago

src/derivepassphrase/sequin.py 287)         base `r` (where `r = base ** k - n`), and attempt to generate
src/derivepassphrase/sequin.py 288)         the requested base `n` integer from the sequence of base `r`
src/derivepassphrase/sequin.py 289)         numbers next.  (This recursion is not attempted if `r` = 1.)
src/derivepassphrase/sequin.py 290)         Otherwise, return the number.
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         291) 
src/sequin/__init__.py         292)         Args:
src/sequin/__init__.py         293)             n:
Marco Ricci Fix miscellaneous small doc...

Marco Ricci authored 2 months ago

src/derivepassphrase/sequin.py 294)                 Generate numbers in the range 0, ..., `n - 1`.
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         295)                 (Inclusive.)  Must be larger than 0.
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         296)             base:
src/sequin/__init__.py         297)                 Use the base `base` sequence as a source for
src/sequin/__init__.py         298)                 pseudorandom numbers.
src/sequin/__init__.py         299) 
src/sequin/__init__.py         300)         Returns:
Marco Ricci Fix miscellaneous small doc...

Marco Ricci authored 2 months ago

src/derivepassphrase/sequin.py 301)             A pseudorandom number in the range 0, ..., `n - 1` if
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         302)             possible, or `n` if the stream is exhausted.
src/sequin/__init__.py         303) 
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         304)         Raises:
src/sequin/__init__.py         305)             ValueError:
src/sequin/__init__.py         306)                 The range is empty.
src/sequin/__init__.py         307) 
Marco Ricci Add unit tests, both new an...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         308)         Examples:
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         309)             >>> seq = Sequin(
src/sequin/__init__.py         310)             ...     [1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1],
src/sequin/__init__.py         311)             ...     is_bitstring=True,
src/sequin/__init__.py         312)             ... )
src/sequin/__init__.py         313)             >>> seq2 = Sequin(
src/sequin/__init__.py         314)             ...     [1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1],
src/sequin/__init__.py         315)             ...     is_bitstring=True,
src/sequin/__init__.py         316)             ... )
Marco Ricci Add unit tests, both new an...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         317)             >>> seq._generate_inner(5)
src/sequin/__init__.py         318)             3
src/sequin/__init__.py         319)             >>> seq._generate_inner(5)
src/sequin/__init__.py         320)             3
src/sequin/__init__.py         321)             >>> seq._generate_inner(5)
src/sequin/__init__.py         322)             1
src/sequin/__init__.py         323)             >>> seq._generate_inner(5)  # error condition: sequin exhausted
src/sequin/__init__.py         324)             5
src/sequin/__init__.py         325) 
src/sequin/__init__.py         326)             Using `n = 1` does not actually consume input bits, and
src/sequin/__init__.py         327)             always works, regardless of sequin exhaustion:
src/sequin/__init__.py         328) 
src/sequin/__init__.py         329)             >>> seq2._generate_inner(1)
src/sequin/__init__.py         330)             0
src/sequin/__init__.py         331)             >>> seq._generate_inner(1)
src/sequin/__init__.py         332)             0
src/sequin/__init__.py         333) 
src/sequin/__init__.py         334)             Using an unsuitable range will raise:
src/sequin/__init__.py         335) 
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         336)             >>> seq2._generate_inner(0)  # doctest: +IGNORE_EXCEPTION_DETAIL
Marco Ricci Add unit tests, both new an...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         337)             Traceback (most recent call last):
src/sequin/__init__.py         338)                 ...
src/sequin/__init__.py         339)             ValueError: invalid target range
src/sequin/__init__.py         340) 
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         341)         """
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         342)         if n < 1:
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py         343)             msg = 'invalid target range'
src/sequin/__init__.py         344)             raise ValueError(msg)
src/sequin/__init__.py         345)         if base < 2:  # noqa: PLR2004
src/sequin/__init__.py         346)             msg = f'invalid base: {base!r}'
src/sequin/__init__.py         347)             raise ValueError(msg)
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         348)         # p = base ** k, where k is the smallest integer such that
src/sequin/__init__.py         349)         # p >= n.  We determine p and k inductively.
src/sequin/__init__.py         350)         p = 1
src/sequin/__init__.py         351)         k = 0
src/sequin/__init__.py         352)         while p < n:
src/sequin/__init__.py         353)             p *= base
src/sequin/__init__.py         354)             k += 1
src/sequin/__init__.py         355)         # The remainder r of p and n is used as the base for rejection
src/sequin/__init__.py         356)         # queue.
src/sequin/__init__.py         357)         r = p - n
src/sequin/__init__.py         358)         # The generated number v is initialized to n because of the
src/sequin/__init__.py         359)         # while loop below.
src/sequin/__init__.py         360)         v = n
src/sequin/__init__.py         361)         while v > n - 1:
src/sequin/__init__.py         362)             list_slice = self._all_or_nothing_shift(k, base=base)
src/sequin/__init__.py         363)             if not list_slice:
Marco Ricci Fix numerous argument type...

Marco Ricci authored 5 months ago

src/sequin/__init__.py         364)                 if n != 1:
src/sequin/__init__.py         365)                     return n
Marco Ricci Fix style issues with ruff...

Marco Ricci authored 3 months ago

src/sequin/__init__.py         366)                 v = 0
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         367)             v = self._big_endian_number(list_slice, base=base)
src/sequin/__init__.py         368)             if v > n - 1:
src/sequin/__init__.py         369)                 # If r is 0, then p == n, so v < n, or rather
src/sequin/__init__.py         370)                 # v <= n - 1.
src/sequin/__init__.py         371)                 assert r > 0
src/sequin/__init__.py         372)                 if r == 1:
src/sequin/__init__.py         373)                     continue
src/sequin/__init__.py         374)                 self._stash(v - n, base=r)
src/sequin/__init__.py         375)                 v = self._generate_inner(n, base=r)
src/sequin/__init__.py         376)         return v
src/sequin/__init__.py         377) 
src/sequin/__init__.py         378)     def _stash(self, value: int, /, *, base: int = 2) -> None:
Marco Ricci Fix miscellaneous small doc...

Marco Ricci authored 2 months ago

src/derivepassphrase/sequin.py 379)         """Stash `value` on the base `base` sequence.
src/derivepassphrase/sequin.py 380) 
src/derivepassphrase/sequin.py 381)         Sets up the base `base` sequence if it does not yet exist.
src/derivepassphrase/sequin.py 382) 
src/derivepassphrase/sequin.py 383)         """
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         384)         if base not in self.bases:
src/sequin/__init__.py         385)             self.bases[base] = collections.deque()
src/sequin/__init__.py         386)         self.bases[base].append(value)
src/sequin/__init__.py         387) 
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         388) 
Marco Ricci Rename SequinExhaustedExcep...

Marco Ricci authored 4 months ago

src/sequin/__init__.py         389) class SequinExhaustedError(Exception):
Marco Ricci Add prototype implementation

Marco Ricci authored 5 months ago

src/sequin/__init__.py         390)     """The sequin is exhausted.
src/sequin/__init__.py         391) 
src/sequin/__init__.py         392)     No more values can be generated from this sequin.
src/sequin/__init__.py         393) 
src/sequin/__init__.py         394)     """
Marco Ricci Reformat everything with ruff

Marco Ricci authored 3 months ago

src/sequin/__init__.py         395) 
Marco Ricci Apply new ruff ruleset to c...

Marco Ricci authored 2 months ago

src/derivepassphrase/sequin.py 396)     def __init__(self) -> None:  # noqa: D107