aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/more-itertools/py3
diff options
context:
space:
mode:
authorAlexander Smirnov <alex@ydb.tech>2025-02-01 00:51:40 +0000
committerAlexander Smirnov <alex@ydb.tech>2025-02-01 00:51:40 +0000
commitbc8b47be9c7f60e7194d1e834f4d57ee1b830d52 (patch)
treeef2e54f1b2e1fad9870b3df17f6ffaaa402a1637 /contrib/python/more-itertools/py3
parent28b42072a94e399b79032d7197a0e9c170e2cff0 (diff)
parent6bdb8392267259f3f148458ab5946e2654631bfc (diff)
downloadydb-bc8b47be9c7f60e7194d1e834f4d57ee1b830d52.tar.gz
Merge branch 'rightlib' into merge-libs-250201-0050
Diffstat (limited to 'contrib/python/more-itertools/py3')
-rw-r--r--contrib/python/more-itertools/py3/.dist-info/METADATA10
-rw-r--r--contrib/python/more-itertools/py3/README.rst3
-rw-r--r--contrib/python/more-itertools/py3/more_itertools/__init__.py2
-rw-r--r--contrib/python/more-itertools/py3/more_itertools/more.py108
-rw-r--r--contrib/python/more-itertools/py3/more_itertools/more.pyi148
-rw-r--r--contrib/python/more-itertools/py3/more_itertools/recipes.py140
-rw-r--r--contrib/python/more-itertools/py3/more_itertools/recipes.pyi68
-rw-r--r--contrib/python/more-itertools/py3/tests/test_more.py46
-rw-r--r--contrib/python/more-itertools/py3/tests/test_recipes.py182
-rw-r--r--contrib/python/more-itertools/py3/ya.make2
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)