diff options
author | Alexander Smirnov <alex@ydb.tech> | 2025-02-01 00:51:40 +0000 |
---|---|---|
committer | Alexander Smirnov <alex@ydb.tech> | 2025-02-01 00:51:40 +0000 |
commit | bc8b47be9c7f60e7194d1e834f4d57ee1b830d52 (patch) | |
tree | ef2e54f1b2e1fad9870b3df17f6ffaaa402a1637 /contrib/python/more-itertools/py3 | |
parent | 28b42072a94e399b79032d7197a0e9c170e2cff0 (diff) | |
parent | 6bdb8392267259f3f148458ab5946e2654631bfc (diff) | |
download | ydb-bc8b47be9c7f60e7194d1e834f4d57ee1b830d52.tar.gz |
Merge branch 'rightlib' into merge-libs-250201-0050
Diffstat (limited to 'contrib/python/more-itertools/py3')
10 files changed, 611 insertions, 98 deletions
diff --git a/contrib/python/more-itertools/py3/.dist-info/METADATA b/contrib/python/more-itertools/py3/.dist-info/METADATA index a06c9b0a57..72be20349f 100644 --- a/contrib/python/more-itertools/py3/.dist-info/METADATA +++ b/contrib/python/more-itertools/py3/.dist-info/METADATA @@ -1,25 +1,26 @@ Metadata-Version: 2.1 Name: more-itertools -Version: 10.5.0 +Version: 10.6.0 Summary: More routines for operating on iterables, beyond itertools Keywords: itertools,iterator,iteration,filter,peek,peekable,chunk,chunked Author-email: Erik Rose <erikrose@grinchcentral.com> -Requires-Python: >=3.8 +Requires-Python: >=3.9 Description-Content-Type: text/x-rst Classifier: Development Status :: 5 - Production/Stable Classifier: Intended Audience :: Developers Classifier: Natural Language :: English Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python :: 3 -Classifier: Programming Language :: Python :: 3.8 Classifier: Programming Language :: Python :: 3.9 Classifier: Programming Language :: Python :: 3.10 Classifier: Programming Language :: Python :: 3.11 Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 Classifier: Programming Language :: Python :: 3 :: Only Classifier: Programming Language :: Python :: Implementation :: CPython Classifier: Programming Language :: Python :: Implementation :: PyPy Classifier: Topic :: Software Development :: Libraries +Project-URL: Documentation, https://more-itertools.readthedocs.io/en/stable/ Project-URL: Homepage, https://github.com/more-itertools/more-itertools ============== @@ -142,6 +143,8 @@ Python iterables. | | `convolve <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.convolve>`_, | | | `dotproduct <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.dotproduct>`_, | | | `factor <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.factor>`_, | +| | `is_prime <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.is_prime>`_, | +| | `nth_prime <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_prime>`_, | | | `matmul <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.matmul>`_, | | | `polynomial_from_roots <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.polynomial_from_roots>`_, | | | `polynomial_derivative <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.polynomial_derivative>`_, | @@ -185,6 +188,7 @@ Python iterables. | | `numeric_range <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.numeric_range>`_, | | | `side_effect <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.side_effect>`_, | | | `iterate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iterate>`_, | +| | `loops <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.loops>`_, | | | `difference <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.difference>`_, | | | `make_decorator <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.make_decorator>`_, | | | `SequenceView <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.SequenceView>`_, | diff --git a/contrib/python/more-itertools/py3/README.rst b/contrib/python/more-itertools/py3/README.rst index 5be8552b2d..059fda9126 100644 --- a/contrib/python/more-itertools/py3/README.rst +++ b/contrib/python/more-itertools/py3/README.rst @@ -118,6 +118,8 @@ Python iterables. | | `convolve <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.convolve>`_, | | | `dotproduct <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.dotproduct>`_, | | | `factor <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.factor>`_, | +| | `is_prime <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.is_prime>`_, | +| | `nth_prime <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.nth_prime>`_, | | | `matmul <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.matmul>`_, | | | `polynomial_from_roots <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.polynomial_from_roots>`_, | | | `polynomial_derivative <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.polynomial_derivative>`_, | @@ -161,6 +163,7 @@ Python iterables. | | `numeric_range <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.numeric_range>`_, | | | `side_effect <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.side_effect>`_, | | | `iterate <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.iterate>`_, | +| | `loops <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.loops>`_, | | | `difference <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.difference>`_, | | | `make_decorator <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.make_decorator>`_, | | | `SequenceView <https://more-itertools.readthedocs.io/en/stable/api.html#more_itertools.SequenceView>`_, | diff --git a/contrib/python/more-itertools/py3/more_itertools/__init__.py b/contrib/python/more-itertools/py3/more_itertools/__init__.py index 583fb57457..4d43d11a63 100644 --- a/contrib/python/more-itertools/py3/more_itertools/__init__.py +++ b/contrib/python/more-itertools/py3/more_itertools/__init__.py @@ -3,4 +3,4 @@ from .more import * # noqa from .recipes import * # noqa -__version__ = '10.5.0' +__version__ = '10.6.0' diff --git a/contrib/python/more-itertools/py3/more_itertools/more.py b/contrib/python/more-itertools/py3/more_itertools/more.py index 64fab26185..2228687ea2 100644 --- a/contrib/python/more-itertools/py3/more_itertools/more.py +++ b/contrib/python/more-itertools/py3/more_itertools/more.py @@ -25,7 +25,7 @@ from itertools import ( from math import comb, e, exp, factorial, floor, fsum, log, log1p, perm, tau from queue import Empty, Queue from random import random, randrange, shuffle, uniform -from operator import itemgetter, mul, sub, gt, lt, le +from operator import itemgetter, mul, sub, gt, lt from sys import hexversion, maxsize from time import monotonic @@ -35,7 +35,9 @@ from .recipes import ( UnequalIterablesError, consume, flatten, + nth, powerset, + sieve, take, unique_everseen, all_equal, @@ -104,6 +106,7 @@ __all__ = [ 'minmax', 'nth_or_last', 'nth_permutation', + 'nth_prime', 'nth_product', 'nth_combination_with_replacement', 'numeric_range', @@ -215,8 +218,8 @@ def first(iterable, default=_marker): return item if default is _marker: raise ValueError( - 'first() was called on an empty iterable, and no ' - 'default value was provided.' + 'first() was called on an empty iterable, ' + 'and no default value was provided.' ) return default @@ -237,15 +240,14 @@ def last(iterable, default=_marker): if isinstance(iterable, Sequence): return iterable[-1] # Work around https://bugs.python.org/issue38525 - elif hasattr(iterable, '__reversed__') and (hexversion != 0x030800F0): + if hasattr(iterable, '__reversed__'): return next(reversed(iterable)) - else: - return deque(iterable, maxlen=1)[-1] + return deque(iterable, maxlen=1)[-1] except (IndexError, TypeError, StopIteration): if default is _marker: raise ValueError( - 'last() was called on an empty iterable, and no default was ' - 'provided.' + 'last() was called on an empty iterable, ' + 'and no default value was provided.' ) return default @@ -569,8 +571,8 @@ def one(iterable, too_short=None, too_long=None): pass else: msg = ( - 'Expected exactly one item in iterable, but got {!r}, {!r}, ' - 'and perhaps more.'.format(first_value, second_value) + f'Expected exactly one item in iterable, but got {first_value!r}, ' + f'{second_value!r}, and perhaps more.' ) raise too_long or ValueError(msg) @@ -631,13 +633,13 @@ def strictly_n(iterable, n, too_short=None, too_long=None): if too_short is None: too_short = lambda item_count: raise_( ValueError, - 'Too few items in iterable (got {})'.format(item_count), + f'Too few items in iterable (got {item_count})', ) if too_long is None: too_long = lambda item_count: raise_( ValueError, - 'Too many items in iterable (got at least {})'.format(item_count), + f'Too many items in iterable (got at least {item_count})', ) it = iter(iterable) @@ -1118,10 +1120,8 @@ def spy(iterable, n=1): [1, 2, 3, 4, 5] """ - it = iter(iterable) - head = take(n, it) - - return head.copy(), chain(head, it) + p, q = tee(iterable) + return take(n, q), p def interleave(*iterables): @@ -1558,8 +1558,8 @@ def split_into(iterable, sizes): [[1], [2, 3], [4], []] When a ``None`` object is encountered in *sizes*, the returned list will - contain items up to the end of *iterable* the same way that itertools.slice - does: + contain items up to the end of *iterable* the same way that + :func:`itertools.slice` does: >>> list(split_into([1,2,3,4,5,6,7,8,9,0], [2,3,None])) [[1, 2], [3, 4, 5], [6, 7, 8, 9, 0]] @@ -2167,13 +2167,11 @@ class numeric_range(abc.Sequence, abc.Hashable): self._start, self._stop, self._step = args elif argc == 0: raise TypeError( - 'numeric_range expected at least ' - '1 argument, got {}'.format(argc) + f'numeric_range expected at least 1 argument, got {argc}' ) else: raise TypeError( - 'numeric_range expected at most ' - '3 arguments, got {}'.format(argc) + f'numeric_range expected at most 3 arguments, got {argc}' ) self._zero = type(self._step)(0) @@ -2236,7 +2234,7 @@ class numeric_range(abc.Sequence, abc.Hashable): else: raise TypeError( 'numeric range indices must be ' - 'integers or slices, not {}'.format(type(key).__name__) + f'integers or slices, not {type(key).__name__}' ) def __hash__(self): @@ -2277,13 +2275,10 @@ class numeric_range(abc.Sequence, abc.Hashable): def __repr__(self): if self._step == 1: - return "numeric_range({}, {})".format( - repr(self._start), repr(self._stop) - ) - else: - return "numeric_range({}, {}, {})".format( - repr(self._start), repr(self._stop), repr(self._step) - ) + return f"numeric_range({self._start!r}, {self._stop!r})" + return ( + f"numeric_range({self._start!r}, {self._stop!r}, {self._step!r})" + ) def __reversed__(self): return iter( @@ -2307,7 +2302,7 @@ class numeric_range(abc.Sequence, abc.Hashable): if r == self._zero: return int(q) - raise ValueError("{} is not in numeric range".format(value)) + raise ValueError(f"{value} is not in numeric range") def _get_by_index(self, i): if i < 0: @@ -2781,7 +2776,7 @@ class SequenceView(Sequence): return len(self._target) def __repr__(self): - return '{}({})'.format(self.__class__.__name__, repr(self._target)) + return f'{self.__class__.__name__}({self._target!r})' class seekable: @@ -3443,8 +3438,8 @@ def only(iterable, default=None, too_long=None): pass else: msg = ( - 'Expected exactly one item in iterable, but got {!r}, {!r}, ' - 'and perhaps more.'.format(first_value, second_value) + f'Expected exactly one item in iterable, but got {first_value!r}, ' + f'{second_value!r}, and perhaps more.' ) raise too_long or ValueError(msg) @@ -3726,9 +3721,11 @@ def _sample_counted(population, k, counts, strict): reservoir = [] for _ in range(k): reservoir.append(feed(0)) - if strict and len(reservoir) < k: - raise ValueError('Sample larger than population') + if strict and len(reservoir) < k: + raise ValueError('Sample larger than population') + + with suppress(StopIteration): W = 1.0 while True: W *= exp(log(random()) / k) @@ -3821,15 +3818,16 @@ def is_sorted(iterable, key=None, reverse=False, strict=False): The function returns ``False`` after encountering the first out-of-order item, which means it may produce results that differ from the built-in - :func:`sorted` function for objects with unusual comparison dynamics. - If there are no out-of-order items, the iterable is exhausted. + :func:`sorted` function for objects with unusual comparison dynamics + (like ``math.nan``). If there are no out-of-order items, the iterable is + exhausted. """ - compare = le if strict else lt it = iterable if (key is None) else map(key, iterable) - it_1, it_2 = tee(it) - next(it_2 if reverse else it_1, None) - - return not any(map(compare, it_1, it_2)) + a, b = tee(it) + next(b, None) + if reverse: + b, a = a, b + return all(map(lt, a, b)) if strict else not any(map(lt, b, a)) class AbortThread(BaseException): @@ -4822,8 +4820,8 @@ def outer_product(func, xs, ys, *args, **kwargs): >>> xs = ['A', 'B', 'A', 'A', 'B', 'B', 'A', 'A', 'B', 'B'] >>> ys = ['X', 'X', 'X', 'Y', 'Z', 'Z', 'Y', 'Y', 'Z', 'Z'] - >>> rows = list(zip(xs, ys)) - >>> count_rows = lambda x, y: rows.count((x, y)) + >>> pair_counts = Counter(zip(xs, ys)) + >>> count_rows = lambda x, y: pair_counts[x, y] >>> list(outer_product(count_rows, sorted(set(xs)), sorted(set(ys)))) [(2, 3, 0), (1, 0, 4)] @@ -4978,3 +4976,23 @@ def doublestarmap(func, iterable): """ for item in iterable: yield func(**item) + + +def _nth_prime_ub(n): + "Upper bound for the nth prime (counting from 1)." + # https://en.wikipedia.org/wiki/Prime-counting_function#Inequalities + return n * log(n * log(n)) if n >= 6 else 11.1 + + +def nth_prime(n): + """Return the nth prime (counting from 0). + + >>> nth_prime(0) + 2 + >>> nth_prime(100) + 547 + """ + if n < 0: + raise ValueError + limit = math.ceil(_nth_prime_ub(n + 1)) + return nth(sieve(limit), n) diff --git a/contrib/python/more-itertools/py3/more_itertools/more.pyi b/contrib/python/more-itertools/py3/more_itertools/more.pyi index 66e6938e13..b840690615 100644 --- a/contrib/python/more-itertools/py3/more_itertools/more.pyi +++ b/contrib/python/more-itertools/py3/more_itertools/more.pyi @@ -5,27 +5,141 @@ from __future__ import annotations import sys import types -from typing import ( - Any, - Callable, +from collections.abc import ( Container, - ContextManager, - Generic, Hashable, - Mapping, Iterable, Iterator, Mapping, - overload, Reversible, Sequence, Sized, - Type, +) +from contextlib import AbstractContextManager +from typing import ( + Any, + Callable, + Generic, TypeVar, + overload, type_check_only, ) from typing_extensions import Protocol +__all__ = [ + 'AbortThread', + 'SequenceView', + 'UnequalIterablesError', + 'adjacent', + 'all_unique', + 'always_iterable', + 'always_reversible', + 'bucket', + 'callback_iter', + 'chunked', + 'chunked_even', + 'circular_shifts', + 'collapse', + 'combination_index', + 'combination_with_replacement_index', + 'consecutive_groups', + 'constrained_batches', + 'consumer', + 'count_cycle', + 'countable', + 'dft', + 'difference', + 'distinct_combinations', + 'distinct_permutations', + 'distribute', + 'divide', + 'doublestarmap', + 'duplicates_everseen', + 'duplicates_justseen', + 'classify_unique', + 'exactly_n', + 'filter_except', + 'filter_map', + 'first', + 'gray_product', + 'groupby_transform', + 'ichunked', + 'iequals', + 'idft', + 'ilen', + 'interleave', + 'interleave_evenly', + 'interleave_longest', + 'intersperse', + 'is_sorted', + 'islice_extended', + 'iterate', + 'iter_suppress', + 'join_mappings', + 'last', + 'locate', + 'longest_common_prefix', + 'lstrip', + 'make_decorator', + 'map_except', + 'map_if', + 'map_reduce', + 'mark_ends', + 'minmax', + 'nth_or_last', + 'nth_permutation', + 'nth_prime', + 'nth_product', + 'nth_combination_with_replacement', + 'numeric_range', + 'one', + 'only', + 'outer_product', + 'padded', + 'partial_product', + 'partitions', + 'peekable', + 'permutation_index', + 'powerset_of_sets', + 'product_index', + 'raise_', + 'repeat_each', + 'repeat_last', + 'replace', + 'rlocate', + 'rstrip', + 'run_length', + 'sample', + 'seekable', + 'set_partitions', + 'side_effect', + 'sliced', + 'sort_together', + 'split_after', + 'split_at', + 'split_before', + 'split_into', + 'split_when', + 'spy', + 'stagger', + 'strip', + 'strictly_n', + 'substrings', + 'substrings_indexes', + 'takewhile_inclusive', + 'time_limited', + 'unique_in_window', + 'unique_to_each', + 'unzip', + 'value_chain', + 'windowed', + 'windowed_complete', + 'with_iter', + 'zip_broadcast', + 'zip_equal', + 'zip_offset', +] + # Type and type variable definitions _T = TypeVar('_T') _T1 = TypeVar('_T1') @@ -38,7 +152,7 @@ _V = TypeVar('_V') _W = TypeVar('_W') _T_co = TypeVar('_T_co', covariant=True) _GenFn = TypeVar('_GenFn', bound=Callable[..., Iterator[Any]]) -_Raisable = BaseException | Type[BaseException] +_Raisable = BaseException | type[BaseException] # The type of isinstance's second argument (from typeshed builtins) if sys.version_info >= (3, 10): @@ -91,7 +205,7 @@ def consumer(func: _GenFn) -> _GenFn: ... def ilen(iterable: Iterable[_T]) -> int: ... def iterate(func: Callable[[_T], _T], start: _T) -> Iterator[_T]: ... def with_iter( - context_manager: ContextManager[Iterable[_T]], + context_manager: AbstractContextManager[Iterable[_T]], ) -> Iterator[_T]: ... def one( iterable: Iterable[_T], @@ -410,7 +524,7 @@ class numeric_range(Generic[_T, _U], Sequence[_T], Hashable, Reversible[_T]): def __len__(self) -> int: ... def __reduce__( self, - ) -> tuple[Type[numeric_range[_T, _U]], tuple[_T, _T, _U]]: ... + ) -> tuple[type[numeric_range[_T, _U]], tuple[_T, _T, _U]]: ... def __repr__(self) -> str: ... def __reversed__(self) -> Iterator[_T]: ... def count(self, value: _T) -> int: ... @@ -567,12 +681,12 @@ def distinct_combinations( def filter_except( validator: Callable[[Any], object], iterable: Iterable[_T], - *exceptions: Type[BaseException], + *exceptions: type[BaseException], ) -> Iterator[_T]: ... def map_except( function: Callable[[Any], _U], iterable: Iterable[_T], - *exceptions: Type[BaseException], + *exceptions: type[BaseException], ) -> Iterator[_U]: ... def map_if( iterable: Iterable[Any], @@ -587,7 +701,7 @@ def _sample_counted( population: Iterator[_T], k: int, counts: Iterable[int], strict: bool ) -> list[_T]: ... def _sample_weighted( - iterator: Iterator[_T], k: int, weights, strict + iterator: Iterator[_T], k: int, weights: Iterator[float], strict: bool ) -> list[_T]: ... def sample( iterable: Iterable[_T], @@ -617,7 +731,7 @@ class callback_iter(Generic[_T], Iterator[_T]): def __enter__(self) -> callback_iter[_T]: ... def __exit__( self, - exc_type: Type[BaseException] | None, + exc_type: type[BaseException] | None, exc_value: BaseException | None, traceback: types.TracebackType | None, ) -> bool | None: ... @@ -797,7 +911,7 @@ def outer_product( ) -> Iterator[tuple[_V, ...]]: ... def iter_suppress( iterable: Iterable[_T], - *exceptions: Type[BaseException], + *exceptions: type[BaseException], ) -> Iterator[_T]: ... def filter_map( func: Callable[[_T], _V | None], @@ -813,3 +927,5 @@ def doublestarmap( ) -> Iterator[_T]: ... def dft(xarr: Sequence[complex]) -> Iterator[complex]: ... def idft(Xarr: Sequence[complex]) -> Iterator[complex]: ... +def _nth_prime_ub(n: int) -> float: ... +def nth_prime(n: int) -> int: ... diff --git a/contrib/python/more-itertools/py3/more_itertools/recipes.py b/contrib/python/more-itertools/py3/more_itertools/recipes.py index 67f76fa899..5a11fc5a30 100644 --- a/contrib/python/more-itertools/py3/more_itertools/recipes.py +++ b/contrib/python/more-itertools/py3/more_itertools/recipes.py @@ -13,7 +13,7 @@ import operator from collections import deque from collections.abc import Sized -from functools import partial, reduce +from functools import lru_cache, partial from itertools import ( chain, combinations, @@ -42,8 +42,10 @@ __all__ = [ 'factor', 'flatten', 'grouper', + 'is_prime', 'iter_except', 'iter_index', + 'loops', 'matmul', 'ncycles', 'nth', @@ -872,8 +874,10 @@ def polynomial_from_roots(roots): >>> polynomial_from_roots(roots) # x^3 - 4 * x^2 - 17 * x + 60 [1, -4, -17, 60] """ - factors = zip(repeat(1), map(operator.neg, roots)) - return list(reduce(convolve, factors, [1])) + poly = [1] + for root in roots: + poly = list(convolve(poly, (1, -root))) + return poly def iter_index(iterable, value, start=0, stop=None): @@ -1005,20 +1009,56 @@ def matmul(m1, m2): return batched(starmap(_sumprod, product(m1, transpose(m2))), n) +def _factor_pollard(n): + # Return a factor of n using Pollard's rho algorithm + gcd = math.gcd + for b in range(1, n - 2): + x = y = 2 + d = 1 + while d == 1: + x = (x * x + b) % n + y = (y * y + b) % n + y = (y * y + b) % n + d = gcd(x - y, n) + if d != n: + return d + raise ValueError('prime or under 5') + + +_primes_below_211 = tuple(sieve(211)) + + def factor(n): """Yield the prime factors of n. >>> list(factor(360)) [2, 2, 2, 3, 3, 5] + + Finds small factors with trial division. Larger factors are + either verified as prime with ``is_prime`` or split into + smaller factors with Pollard's rho algorithm. """ - for prime in sieve(math.isqrt(n) + 1): + + # Corner case reduction + if n < 2: + return + + # Trial division reduction + for prime in _primes_below_211: while not n % prime: yield prime n //= prime - if n == 1: - return - if n > 1: - yield n + + # Pollard's rho reduction + primes = [] + todo = [n] if n > 1 else [] + for n in todo: + if n < 211**2 or is_prime(n): + primes.append(n) + else: + fact = _factor_pollard(n) + todo += (fact, n // fact) + yield from sorted(primes) def polynomial_eval(coefficients, x): @@ -1073,3 +1113,87 @@ def totient(n): for prime in set(factor(n)): n -= n // prime return n + + +# Miller–Rabin primality test: https://oeis.org/A014233 +_perfect_tests = [ + (2047, (2,)), + (9080191, (31, 73)), + (4759123141, (2, 7, 61)), + (1122004669633, (2, 13, 23, 1662803)), + (2152302898747, (2, 3, 5, 7, 11)), + (3474749660383, (2, 3, 5, 7, 11, 13)), + (18446744073709551616, (2, 325, 9375, 28178, 450775, 9780504, 1795265022)), + ( + 3317044064679887385961981, + (2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41), + ), +] + + +@lru_cache +def _shift_to_odd(n): + 'Return s, d such that 2**s * d == n' + s = ((n - 1) ^ n).bit_length() - 1 + d = n >> s + assert (1 << s) * d == n and d & 1 and s >= 0 + return s, d + + +def _strong_probable_prime(n, base): + assert (n > 2) and (n & 1) and (2 <= base < n) + + s, d = _shift_to_odd(n - 1) + + x = pow(base, d, n) + if x == 1 or x == n - 1: + return True + + for _ in range(s - 1): + x = x * x % n + if x == n - 1: + return True + + return False + + +def is_prime(n): + """Return ``True`` if *n* is prime and ``False`` otherwise. + + >>> is_prime(37) + True + >>> is_prime(3 * 13) + False + >>> is_prime(18_446_744_073_709_551_557) + True + + This function uses the Miller-Rabin primality test, which can return false + positives for very large inputs. For values of *n* below 10**24 + there are no false positives. For larger values, there is less than + a 1 in 2**128 false positive rate. Multiple tests can further reduce the + chance of a false positive. + """ + if n < 17: + return n in {2, 3, 5, 7, 11, 13} + if not (n & 1 and n % 3 and n % 5 and n % 7 and n % 11 and n % 13): + return False + for limit, bases in _perfect_tests: + if n < limit: + break + else: + bases = [randrange(2, n - 1) for i in range(64)] + return all(_strong_probable_prime(n, base) for base in bases) + + +def loops(n): + """Returns an iterable with *n* elements for efficient looping. + Like ``range(n)`` but doesn't create integers. + + >>> i = 0 + >>> for _ in loops(5): + ... i += 1 + >>> i + 5 + + """ + return repeat(None, n) diff --git a/contrib/python/more-itertools/py3/more_itertools/recipes.pyi b/contrib/python/more-itertools/py3/more_itertools/recipes.pyi index 739acec05f..e404f4d3df 100644 --- a/contrib/python/more-itertools/py3/more_itertools/recipes.pyi +++ b/contrib/python/more-itertools/py3/more_itertools/recipes.pyi @@ -2,17 +2,65 @@ from __future__ import annotations +from collections.abc import Iterable, Iterator, Sequence from typing import ( Any, Callable, - Iterable, - Iterator, - overload, - Sequence, - Type, TypeVar, + overload, ) +__all__ = [ + 'all_equal', + 'batched', + 'before_and_after', + 'consume', + 'convolve', + 'dotproduct', + 'first_true', + 'factor', + 'flatten', + 'grouper', + 'is_prime', + 'iter_except', + 'iter_index', + 'loops', + 'matmul', + 'ncycles', + 'nth', + 'nth_combination', + 'padnone', + 'pad_none', + 'pairwise', + 'partition', + 'polynomial_eval', + 'polynomial_from_roots', + 'polynomial_derivative', + 'powerset', + 'prepend', + 'quantify', + 'reshape', + 'random_combination_with_replacement', + 'random_combination', + 'random_permutation', + 'random_product', + 'repeatfunc', + 'roundrobin', + 'sieve', + 'sliding_window', + 'subslices', + 'sum_of_squares', + 'tabulate', + 'tail', + 'take', + 'totient', + 'transpose', + 'triplewise', + 'unique', + 'unique_everseen', + 'unique_justseen', +] + # Type and type variable definitions _T = TypeVar('_T') _T1 = TypeVar('_T1') @@ -69,13 +117,13 @@ def unique( @overload def iter_except( func: Callable[[], _T], - exception: Type[BaseException] | tuple[Type[BaseException], ...], + exception: type[BaseException] | tuple[type[BaseException], ...], first: None = ..., ) -> Iterator[_T]: ... @overload def iter_except( func: Callable[[], _T], - exception: Type[BaseException] | tuple[Type[BaseException], ...], + exception: type[BaseException] | tuple[type[BaseException], ...], first: Callable[[], _U], ) -> Iterator[_T | _U]: ... @overload @@ -129,8 +177,14 @@ def reshape( matrix: Iterable[Iterable[_T]], cols: int ) -> Iterator[tuple[_T, ...]]: ... def matmul(m1: Sequence[_T], m2: Sequence[_T]) -> Iterator[tuple[_T]]: ... +def _factor_trial(n: int) -> Iterator[int]: ... +def _factor_pollard(n: int) -> int: ... def factor(n: int) -> Iterator[int]: ... def polynomial_eval(coefficients: Sequence[_T], x: _U) -> _U: ... def sum_of_squares(it: Iterable[_T]) -> _T: ... def polynomial_derivative(coefficients: Sequence[_T]) -> list[_T]: ... def totient(n: int) -> int: ... +def _shift_to_odd(n: int) -> tuple[int, int]: ... +def _strong_probable_prime(n: int, base: int) -> bool: ... +def is_prime(n: int) -> bool: ... +def loops(n: int) -> Iterator[None]: ... diff --git a/contrib/python/more-itertools/py3/tests/test_more.py b/contrib/python/more-itertools/py3/tests/test_more.py index 1a70ea08e5..bfbf583f28 100644 --- a/contrib/python/more-itertools/py3/tests/test_more.py +++ b/contrib/python/more-itertools/py3/tests/test_more.py @@ -3836,7 +3836,7 @@ class _FrozenMultiset(Set): return hash(self._collection) def __repr__(self): - return "FrozenSet([{}]".format(", ".join(repr(x) for x in iter(self))) + return f'FrozenSet([{", ".join(repr(x) for x in iter(self))}]' class SetPartitionsTests(TestCase): @@ -4321,6 +4321,33 @@ class SampleTests(TestCase): # The observed largest difference in 10,000 simulations was 4.337999 self.assertTrue(difference_in_means < 4.4) + def test_error_cases(self): + + # weights and counts are mutally exclusive + with self.assertRaises(TypeError): + mi.sample( + 'abcde', 3, weights=[1, 2, 3, 4, 5], counts=[1, 2, 3, 4, 5] + ) + + # Weighted sample larger than population + with self.assertRaises(ValueError): + mi.sample('abcde', 10, weights=[1, 2, 3, 4, 5], strict=True) + + # Counted sample larger than population + with self.assertRaises(ValueError): + mi.sample('abcde', 10, counts=[1, 1, 1, 1, 1], strict=True) + + +class BarelySortable: + def __init__(self, value): + self.value = value + + def __lt__(self, other): + return self.value < other.value + + def __int__(self): + return int(self.value) + class IsSortedTests(TestCase): def test_basic(self): @@ -4330,7 +4357,6 @@ class IsSortedTests(TestCase): ([1, 2, 3], {}, True), ([1, 1, 2, 3], {}, True), ([1, 10, 2, 3], {}, False), - ([3, float('nan'), 1, 2], {}, True), (['1', '10', '2', '3'], {}, True), (['1', '10', '2', '3'], {'key': int}, False), ([1, 2, 3], {'reverse': True}, False), @@ -4362,17 +4388,6 @@ class IsSortedTests(TestCase): {'strict': True, 'key': int, 'reverse': True}, False, ), - # We'll do the same weird thing as Python here - (['nan', 0, 'nan', 0], {'key': float}, True), - ([0, 'nan', 0, 'nan'], {'key': float}, True), - (['nan', 0, 'nan', 0], {'key': float, 'reverse': True}, True), - ([0, 'nan', 0, 'nan'], {'key': float, 'reverse': True}, True), - ([0, 'nan', 0, 'nan'], {'strict': True, 'key': float}, True), - ( - ['nan', 0, 'nan', 0], - {'strict': True, 'key': float, 'reverse': True}, - True, - ), ]: key = kwargs.get('key', None) reverse = kwargs.get('reverse', False) @@ -4382,7 +4397,10 @@ class IsSortedTests(TestCase): iterable=iterable, key=key, reverse=reverse, strict=strict ): mi_result = mi.is_sorted( - iter(iterable), key=key, reverse=reverse, strict=strict + map(BarelySortable, iterable), + key=key, + reverse=reverse, + strict=strict, ) sorted_iterable = sorted(iterable, key=key, reverse=reverse) diff --git a/contrib/python/more-itertools/py3/tests/test_recipes.py b/contrib/python/more-itertools/py3/tests/test_recipes.py index 684a6fcd0b..a810b8de1e 100644 --- a/contrib/python/more-itertools/py3/tests/test_recipes.py +++ b/contrib/python/more-itertools/py3/tests/test_recipes.py @@ -4,7 +4,7 @@ from fractions import Fraction from functools import reduce from itertools import combinations, count, groupby, permutations from operator import mul -from math import factorial +from math import comb, factorial from sys import version_info from unittest import TestCase, skipIf from unittest.mock import patch @@ -923,6 +923,12 @@ class PolynomialFromRootsTests(TestCase): actual = mi.polynomial_from_roots(roots) self.assertEqual(actual, expected) + def test_large(self): + n = 1_500 + actual = mi.polynomial_from_roots([-1] * n) + expected = [comb(n, k) for k in range(n + 1)] + self.assertEqual(actual, expected) + class PolynomialEvalTests(TestCase): def test_basic(self): @@ -1147,8 +1153,12 @@ class FactorTests(TestCase): (6, [2, 3]), (360, [2, 2, 2, 3, 3, 5]), (128_884_753_939, [128_884_753_939]), - (999953 * 999983, [999953, 999983]), - (909_909_090_909, [3, 3, 7, 13, 13, 751, 113797]), + (999_953 * 999_983, [999_953, 999_983]), + (909_909_090_909, [3, 3, 7, 13, 13, 751, 1_137_97]), + ( + 1_647_403_876_764_101_672_307_088, + [2, 2, 2, 2, 19, 23, 109471, 13571009, 158594251], + ), ): with self.subTest(n=n): actual = list(mi.factor(n)) @@ -1209,3 +1219,169 @@ class TotientTests(TestCase): ): with self.subTest(n=n): self.assertEqual(mi.totient(n), expected) + + +class PrimeFunctionTests(TestCase): + def test_is_prime_pseudoprimes(self): + # Carmichael number that strong pseudoprime to prime bases < 307 + # https://doi.org/10.1006/jsco.1995.1042 + p = 29674495668685510550154174642905332730771991799853043350995075531276838753171770199594238596428121188033664754218345562493168782883 # noqa:E501 + gnarly_carmichael = (313 * (p - 1) + 1) * (353 * (p - 1) + 1) + + for n in ( + # Least Carmichael number with n prime factors: + # https://oeis.org/A006931 + 561, + 41041, + 825265, + 321197185, + 5394826801, + 232250619601, + 9746347772161, + 1436697831295441, + 60977817398996785, + 7156857700403137441, + 1791562810662585767521, + 87674969936234821377601, + 6553130926752006031481761, + 1590231231043178376951698401, + # Carmichael numbers with exactly 4 prime factors: + # https://oeis.org/A074379 + 41041, + 62745, + 63973, + 75361, + 101101, + 126217, + 172081, + 188461, + 278545, + 340561, + 449065, + 552721, + 656601, + 658801, + 670033, + 748657, + 838201, + 852841, + 997633, + 1033669, + 1082809, + 1569457, + 1773289, + 2100901, + 2113921, + 2433601, + 2455921, + # Lucas-Carmichael numbers: + # https://oeis.org/A006972 + 399, + 935, + 2015, + 2915, + 4991, + 5719, + 7055, + 8855, + 12719, + 18095, + 20705, + 20999, + 22847, + 29315, + 31535, + 46079, + 51359, + 60059, + 63503, + 67199, + 73535, + 76751, + 80189, + 81719, + 88559, + 90287, + # Strong pseudoprimes to bases 2, 3 and 5: + # https://oeis.org/A056915 + 25326001, + 161304001, + 960946321, + 1157839381, + 3215031751, + 3697278427, + 5764643587, + 6770862367, + 14386156093, + 15579919981, + 18459366157, + 19887974881, + 21276028621, + 27716349961, + 29118033181, + 37131467521, + 41752650241, + 42550716781, + 43536545821, + # Strong pseudoprimes to bases 2, 3, 5, and 7: + # https://oeis.org/A211112 + 39365185894561, + 52657210792621, + 11377272352951, + 15070413782971, + 3343433905957, + 16603327018981, + 3461715915661, + 52384617784801, + 3477707481751, + 18996486073489, + 55712149574381, + gnarly_carmichael, + ): + with self.subTest(n=n): + self.assertFalse(mi.is_prime(n)) + + def test_primes(self): + for i, n in enumerate(mi.sieve(10**5)): + with self.subTest(n=n): + self.assertTrue(mi.is_prime(n)) + self.assertEqual(mi.nth_prime(i), n) + + self.assertFalse(mi.is_prime(-1)) + with self.assertRaises(ValueError): + mi.nth_prime(-1) + + def test_special_primes(self): + for n in ( + # Mersenee primes: + # https://oeis.org/A211112 + 3, + 7, + 31, + 127, + 8191, + 131071, + 524287, + 2147483647, + 2305843009213693951, + 618970019642690137449562111, + 162259276829213363391578010288127, + 170141183460469231731687303715884105727, + # Various big primes: + # https://bigprimes.org/ + 7990614013, + 80358337843874809987, + 814847562949580526031364519741, + 1982427225022428178169740526258124929077, + 91828213828508622559862344537590739566883686537727, + 406414746815201693481517584049440077164779143248351060891669, + ): + with self.subTest(n=n): + self.assertTrue(mi.is_prime(n)) + + +class LoopsTests(TestCase): + def test_basic(self): + self.assertTrue( + all(list(mi.loops(n)) == [None] * n for n in range(-10, 10)) + ) diff --git a/contrib/python/more-itertools/py3/ya.make b/contrib/python/more-itertools/py3/ya.make index 45df93175b..b604dff015 100644 --- a/contrib/python/more-itertools/py3/ya.make +++ b/contrib/python/more-itertools/py3/ya.make @@ -2,7 +2,7 @@ PY3_LIBRARY() -VERSION(10.5.0) +VERSION(10.6.0) LICENSE(MIT) |